环境

  • MacOS 10.14.6
  • JDK1.8

源码链接:

https://github.com/zuiyu-main...

应用keytool工具生成签名文件

https://mp.weixin.qq.com/s?__...

生成私钥公钥

  • 应用JDK自带的keytool工具生成签名

    keytool -genkeypair -keysize 1024 -validity 3650 -alias "zuiyuPrivateKey" -keypass "zuiyu_private_password_1234" -keystore "zuiyuPrivateKeys.keystore" -storepass "zuiyu_public_password_1234" -dname "CN=zuiyu,OU=zuiyu,O=zuiyu,L=BJ,ST=BJ,C=CN"
  • 导出签名文件 zuiyuCertfile.cer

    keytool -exportcert -alias "zuiyuPrivateKey" -keystore "zuiyuPrivateKeys.keystore" -storepass "zuiyu_public_password_1234" -file "zuiyuCertfile.cer"
  • 导入签名文件

    keytool -import -alias "zuiyuPublicCert" -file "zuiyuCertfile.cer" -keystore "zuiyuPublicCerts.keystore" -storepass "zuiyu_public_password_1234"
  • 帮忙命令(依据须要食用)

    # 删除keytool -delete -alias zuiyuPrivateKey -keystore "zuiyuPrivateKeys.keystore" -storepass "zuiyu_public_password_1234"# 查看keytool -list -v -keystore zuiyuPrivateKeys.keystore -storepass "zuiyu_public_password_1234"
  • 最初

    上述命令执行实现之后,会在以后门路下生成三个文件,别离是:zuiyuPrivateKeys.keystorezuiyuPublicCerts.keystorezuiyuCertfile.cer。其中文件zuiyuCertfile.cer不再须要能够删除,文件zuiyuPrivateKeys.keystore用于以后的 license-server 我的项目给客户生成license文件,而文件zuiyuPublicCerts.keystore则随利用代码部署到客户服务器,用户解密license文件并校验其许可信息。

license-common

如下license-common 内容较长,全副代码都有,可间接粘贴

如下license-common 内容较长,全副代码都有,可间接粘贴

如下license-common 内容较长,全副代码都有,可间接粘贴

可间接跳转源码GitHub

https://github.com/zuiyu-main...
  • 新建一个license-common 模块
  • pom.xml 中退出license相干依赖

                    <dependency>            <groupId>org.springframework.boot</groupId>            <artifactId>spring-boot-starter-web</artifactId>        </dependency>        <dependency>            <groupId>com.alibaba</groupId>            <artifactId>fastjson</artifactId>            <version>1.2.47</version>        </dependency>        <!-- License -->        <dependency>            <groupId>de.schlichtherle.truelicense</groupId>            <artifactId>truelicense-core</artifactId>            <version>1.33</version>        </dependency>        <dependency>            <groupId>org.apache.commons</groupId>            <artifactId>commons-lang3</artifactId>            <version>3.7</version>        </dependency>
  • 批改pom.xml中打包插件配置

    <build>        <plugins>            <plugin>                <groupId>org.apache.maven.plugins</groupId>                <artifactId>maven-compiler-plugin</artifactId>                <version>3.8.1</version>                <configuration>                    <source>1.8</source>                    <target>1.8</target>                    <encoding>UTF-8</encoding>                </configuration>            </plugin>        </plugins>    </build>
  • 定义一个受权文件信息类 LicenseCreatorParam

    public class LicenseCreatorParam implements Serializable {    private static final long serialVersionUID = -7793154252684580872L;    /**     * 证书subject     */    private String subject;    /**     * 密钥别称     */    private String privateAlias;    /**     * 密钥明码(须要妥善保存,不能让使用者晓得)     */    private String keyPass;    /**     * 拜访秘钥库的明码     */    private String storePass;    /**     * 证书生成门路     */    private String licensePath;    /**     * 密钥库存储门路     */    private String privateKeysStorePath;    /**     * 证书失效工夫     */    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")    private Date issuedTime = new Date();    /**     * 证书生效工夫     */    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")    private Date expiryTime;    /**     * 用户类型     */    private String consumerType = "user";    /**     * 用户数量     */    private Integer consumerAmount = 1;    /**     * 形容信息     */    private String description = "";    /**     * 额定的服务器硬件校验信息     */    private LicenseCheckModel licenseCheckModel;    // 省略get set}
  • 定义一个受权文件中零碎信息类 LicenseCheckModel

    public class LicenseCheckModel {//    private static final long serialVersionUID = 8600137500316662317L;    /**     * 可被容许的IP地址     */    private Boolean checkIp;    private List<String> ipAddress;    private Boolean checkMac;    /**     * 可被容许的MAC地址     */    private List<String> macAddress;    /**     * 可被容许的CPU序列号     */    private String cpuSerial;    private Boolean checkCpu;    /**     * 可被容许的主板序列号     */    private String mainBoardSerial;    private Boolean checkMainBoard;    // 省略get set}
  • 定义一个校验签名文件的信息类LicenseVerifyParam

    public class LicenseVerifyParam {    /**     * 证书subject     */    private String subject;    /**     * 公钥别称     */    private String publicAlias;    /**     * 拜访公钥库的明码     */    private String storePass;    /**     * 证书生成门路     */    private String licensePath;    /**     * 密钥库存储门路     */    private String publicKeysStorePath;    public LicenseVerifyParam() {    }    public LicenseVerifyParam(String subject, String publicAlias, String storePass, String licensePath, String publicKeysStorePath) {        this.subject = subject;        this.publicAlias = publicAlias;        this.storePass = storePass;        this.licensePath = licensePath;        this.publicKeysStorePath = publicKeysStorePath;    }    // 省略get set}
  • 定义获取客户服务器信息的抽象类AbstractServerInfos

    public abstract class AbstractServerInfos {    public static Logger log = LoggerFactory.getLogger(AbstractServerInfos.class);    /**     * 组装须要额定校验的License参数     *     * @return demo.LicenseCheckModel     */    public LicenseCheckModel getServerInfos() {        LicenseCheckModel result = new LicenseCheckModel();        try {            result.setIpAddress(this.getIpAddress());            result.setMacAddress(this.getMacAddress());            result.setCpuSerial(this.getCPUSerial());            result.setMainBoardSerial(this.getMainBoardSerial());        } catch (Exception e) {            log.error("获取服务器硬件信息失败", e);        }        return result;    }    /**     * 获取IP地址     *     * @return java.util.List<java.lang.String>     */    protected abstract List<String> getIpAddress() throws Exception;    /**     * 获取Mac地址     *     * @return java.util.List<java.lang.String>     */    protected abstract List<String> getMacAddress() throws Exception;    /**     * 获取CPU序列号     *     * @return java.lang.String     */    protected abstract String getCPUSerial() throws Exception;    /**     * 获取主板序列号     *     * @return java.lang.String     */    protected abstract String getMainBoardSerial() throws Exception;    /**     * 获取以后服务器所有符合条件的InetAddress     *     * @return java.util.List<java.net.InetAddress>     */    protected List<InetAddress> getLocalAllInetAddress() throws Exception {        List<InetAddress> result = new ArrayList<>(4);        // 遍历所有的网络接口        for (Enumeration networkInterfaces = NetworkInterface.getNetworkInterfaces(); networkInterfaces.hasMoreElements(); ) {            NetworkInterface iface = (NetworkInterface) networkInterfaces.nextElement();            // 在所有的接口下再遍历IP            for (Enumeration inetAddresses = iface.getInetAddresses(); inetAddresses.hasMoreElements(); ) {                InetAddress inetAddr = (InetAddress) inetAddresses.nextElement();                //排除LoopbackAddress、SiteLocalAddress、LinkLocalAddress、MulticastAddress类型的IP地址                if (!inetAddr.isLoopbackAddress() /*&& !inetAddr.isSiteLocalAddress()*/                        && !inetAddr.isLinkLocalAddress() && !inetAddr.isMulticastAddress()) {                    result.add(inetAddr);                }            }        }        return result;    }    /**     * 获取某个网络接口的Mac地址     *     * @param     * @return void     */    protected String getMacByInetAddress(InetAddress inetAddr) {        try {            byte[] mac = NetworkInterface.getByInetAddress(inetAddr).getHardwareAddress();            StringBuffer stringBuffer = new StringBuffer();            for (int i = 0; i < mac.length; i++) {                if (i != 0) {                    stringBuffer.append("-");                }                //将十六进制byte转化为字符串                String temp = Integer.toHexString(mac[i] & 0xff);                if (temp.length() == 1) {                    stringBuffer.append("0" + temp);                } else {                    stringBuffer.append(temp);                }            }            return stringBuffer.toString().toUpperCase();        } catch (SocketException e) {            log.error("获取某个网络接口的Mac地址异样", e);        }        return null;    }}
  • 定义不同零碎获取零碎信息的办法LinuxServerInfos,WindowsServerInfos

    • windows
    public class WindowsServerInfos extends AbstractServerInfos {    @Override    protected List<String> getIpAddress() throws Exception {        List<String> result = null;        //获取所有网络接口        List<InetAddress> inetAddresses = getLocalAllInetAddress();        if (inetAddresses != null && inetAddresses.size() > 0) {            result = inetAddresses.stream().map(InetAddress::getHostAddress).distinct().map(String::toLowerCase).collect(Collectors.toList());        }        return result;    }    @Override    protected List<String> getMacAddress() throws Exception {        List<String> result = null;        //1. 获取所有网络接口        List<InetAddress> inetAddresses = getLocalAllInetAddress();        if (inetAddresses != null && inetAddresses.size() > 0) {            //2. 获取所有网络接口的Mac地址            result = inetAddresses.stream().map(this::getMacByInetAddress).distinct().collect(Collectors.toList());        }        return result;    }    @Override    protected String getCPUSerial() throws Exception {        //序列号        String serialNumber = "";        //应用WMIC获取CPU序列号        Process process = Runtime.getRuntime().exec("wmic cpu get processorid");        process.getOutputStream().close();        Scanner scanner = new Scanner(process.getInputStream());        if (scanner != null && scanner.hasNext()) {            scanner.next();        }        if (scanner.hasNext()) {            serialNumber = scanner.next().trim();        }        scanner.close();        return serialNumber;    }    @Override    protected String getMainBoardSerial() throws Exception {        //序列号        String serialNumber = "";        //应用WMIC获取主板序列号        Process process = Runtime.getRuntime().exec("wmic baseboard get serialnumber");        process.getOutputStream().close();        Scanner scanner = new Scanner(process.getInputStream());        if (scanner != null && scanner.hasNext()) {            scanner.next();        }        if (scanner.hasNext()) {            serialNumber = scanner.next().trim();        }        scanner.close();        return serialNumber;    }}
    • linux
      public class LinuxServerInfos extends AbstractServerInfos {        @Override      protected List<String> getIpAddress() throws Exception {          List<String> result = null;            //获取所有网络接口          List<InetAddress> inetAddresses = getLocalAllInetAddress();            if (inetAddresses != null && inetAddresses.size() > 0) {              result = inetAddresses.stream().map(InetAddress::getHostAddress).distinct().map(String::toLowerCase).collect(Collectors.toList());          }            return result;      }        @Override      protected List<String> getMacAddress() throws Exception {          List<String> result = null;            //1. 获取所有网络接口          List<InetAddress> inetAddresses = getLocalAllInetAddress();            if (inetAddresses != null && inetAddresses.size() > 0) {              //2. 获取所有网络接口的Mac地址              result = inetAddresses.stream().map(this::getMacByInetAddress).distinct().collect(Collectors.toList());          }            return result;      }        @Override      protected String getCPUSerial() throws Exception {          //序列号          String serialNumber = "";            //应用dmidecode命令获取CPU序列号          String[] shell = {"/bin/bash", "-c", "dmidecode -t processor | grep 'ID' | awk -F ':' '{print $2}' | head -n 1"};          Process process = Runtime.getRuntime().exec(shell);          process.getOutputStream().close();            BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()));            if (null == reader.readLine()) {              return serialNumber;          }          String line = reader.readLine().trim();          if (StringUtils.isNotBlank(line)) {              serialNumber = line;          }            reader.close();          return serialNumber;      }        @Override      protected String getMainBoardSerial() throws Exception {          //序列号          String serialNumber = "";            //应用dmidecode命令获取主板序列号          String[] shell = {"/bin/bash", "-c", "dmidecode | grep 'Serial Number' | awk -F ':' '{print $2}' | head -n 1"};          Process process = Runtime.getRuntime().exec(shell);          process.getOutputStream().close();            BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()));          if (null == reader.readLine()) {              return serialNumber;          }          String line = reader.readLine().trim();          if (StringUtils.isNotBlank(line)) {              serialNumber = line;          }            reader.close();          return serialNumber;      }  }
  • 减少CustomLicenseManager ,用于自定义参数信息验证

    public class CustomLicenseManager extends LicenseManager {    public static Logger log = LoggerFactory.getLogger(CustomLicenseManager.class);    //XML编码    private static final String XML_CHARSET = "UTF-8";    //默认BUFSIZE    private static final int DEFAULT_BUFSIZE = 8 * 1024;    public CustomLicenseManager() {    }    public CustomLicenseManager(LicenseParam param) {        super(param);    }    /**     * 复写create办法     *     * @return byte[]     */    @Override    protected synchronized byte[] create(            LicenseContent content,            LicenseNotary notary)            throws Exception {        initialize(content);        this.validateCreate(content);        final GenericCertificate certificate = notary.sign(content);        return getPrivacyGuard().cert2key(certificate);    }    /**     * 复写install办法,其中validate办法调用本类中的validate办法,校验IP地址、Mac地址等其余信息     *     * @param     * @return de.schlichtherle.license.LicenseContent     */    @Override    protected synchronized LicenseContent install(            final byte[] key,            final LicenseNotary notary)            throws Exception {        final GenericCertificate certificate = getPrivacyGuard().key2cert(key);        notary.verify(certificate);        final LicenseContent content = (LicenseContent) this.load(certificate.getEncoded());        this.validate(content);        setLicenseKey(key);        setCertificate(certificate);        return content;    }    /**     * 复写verify办法,调用本类中的validate办法,校验IP地址、Mac地址等其余信息     *     * @param     * @return de.schlichtherle.license.LicenseContent     */    @Override    protected synchronized LicenseContent verify(final LicenseNotary notary)            throws Exception {        GenericCertificate certificate = getCertificate();        // Load license key from preferences,        final byte[] key = getLicenseKey();        if (null == key) {            throw new NoLicenseInstalledException(getLicenseParam().getSubject());        }        certificate = getPrivacyGuard().key2cert(key);        notary.verify(certificate);        final LicenseContent content = (LicenseContent) this.load(certificate.getEncoded());        this.validate(content);        setCertificate(certificate);        return content;    }    /**     * 校验生成证书的参数信息     *     * @param content 证书注释     */    protected synchronized void validateCreate(final LicenseContent content)            throws LicenseContentException {        final LicenseParam param = getLicenseParam();        final Date now = new Date();        final Date notBefore = content.getNotBefore();        final Date notAfter = content.getNotAfter();        if (null != notAfter && now.after(notAfter)) {            throw new LicenseContentException("证书生效工夫不能早于以后工夫");        }        if (null != notBefore && null != notAfter && notAfter.before(notBefore)) {            throw new LicenseContentException("证书失效工夫不能晚于证书生效工夫");        }        final String consumerType = content.getConsumerType();        if (null == consumerType) {            throw new LicenseContentException("用户类型不能为空");        }    }    /**     * 复写validate办法,减少IP地址、Mac地址等其余信息校验     *     * @param content LicenseContent     */    @Override    protected synchronized void validate(final LicenseContent content)            throws LicenseContentException {        //1. 首先调用父类的validate办法        super.validate(content);        //2. 而后校验自定义的License参数        //License中可被容许的参数信息        LicenseCheckModel expectedCheckModel = (LicenseCheckModel) content.getExtra();        if (expectedCheckModel != null) {            //以后服务器实在的参数信息            LicenseCheckModel serverCheckModel = getServerInfos();            if (serverCheckModel != null) {                //校验IP地址                if (expectedCheckModel.getCheckIp() && !checkIpAddress(expectedCheckModel.getIpAddress(), serverCheckModel.getIpAddress())) {                    throw new LicenseContentException("以后服务器的IP没在受权范畴内");                }                //校验Mac地址                if (expectedCheckModel.getCheckMac() && !checkIpAddress(expectedCheckModel.getMacAddress(), serverCheckModel.getMacAddress())) {                    throw new LicenseContentException("以后服务器的Mac地址没在受权范畴内");                }                //校验主板序列号                if (expectedCheckModel.getCheckMainBoard() && !checkSerial(expectedCheckModel.getMainBoardSerial(), serverCheckModel.getMainBoardSerial())) {                    throw new LicenseContentException("以后服务器的主板序列号没在受权范畴内");                }                //校验CPU序列号                if (expectedCheckModel.getCheckCpu() && !checkSerial(expectedCheckModel.getCpuSerial(), serverCheckModel.getCpuSerial())) {                    throw new LicenseContentException("以后服务器的CPU序列号没在受权范畴内");                }            } else {                throw new LicenseContentException("不能获取服务器硬件信息");            }        }    }    /**     * 重写XMLDecoder解析XML     *     * @param encoded XML类型字符串     * @return java.lang.Object     */    private Object load(String encoded) {//         encoded = encoded.replace("com.modules.entity.LicenseCheckModel", "com.zuiyu.client.bean.LicenseCheckModel");        BufferedInputStream inputStream = null;        XMLDecoder decoder = null;        try {            inputStream = new BufferedInputStream(new ByteArrayInputStream(encoded.getBytes(XML_CHARSET)));            decoder = new XMLDecoder(new BufferedInputStream(inputStream, DEFAULT_BUFSIZE), null, null);            return decoder.readObject();        } catch (UnsupportedEncodingException e) {            e.printStackTrace();        } finally {            try {                if (decoder != null) {                    decoder.close();                }                if (inputStream != null) {                    inputStream.close();                }            } catch (Exception e) {                log.error("XMLDecoder解析XML失败", e);            }        }        return null;    }    /**     * 获取以后服务器须要额定校验的License参数     *     * @return demo.LicenseCheckModel     */    private LicenseCheckModel getServerInfos() {        //操作系统类型        String osName = System.getProperty("os.name").toLowerCase();        AbstractServerInfos abstractServerInfos = null;        //依据不同操作系统类型抉择不同的数据获取办法        if (osName.startsWith("windows")) {            abstractServerInfos = new WindowsServerInfos();        } else if (osName.startsWith("linux")) {            abstractServerInfos = new LinuxServerInfos();        } else {//其余服务器类型            abstractServerInfos = new LinuxServerInfos();        }        return abstractServerInfos.getServerInfos();    }    /**     * 校验以后服务器的IP/Mac地址是否在可被容许的IP范畴内<br/>     * 如果存在IP在可被容许的IP/Mac地址范畴内,则返回true     *     * @return boolean     */    private boolean checkIpAddress(List<String> expectedList, List<String> serverList) {        if (expectedList != null && expectedList.size() > 0) {            if (serverList != null && serverList.size() > 0) {                for (String expected : expectedList) {                    if (serverList.contains(expected.trim())) {                        return true;                    }                }            }            return false;        } else {            return true;        }    }    /**     * 校验以后服务器硬件(主板、CPU等)序列号是否在可容许范畴内     *     * @param     * @return boolean     */    private boolean checkSerial(String expectedSerial, String serverSerial) {        if (StringUtils.isNotBlank(expectedSerial)) {            if (StringUtils.isNotBlank(serverSerial)) {                if (expectedSerial.equals(serverSerial)) {                    return true;                }            }            return false;        } else {            return true;        }    }}
  • 定义 CustomKeyStoreParam,用于受权文件长久化磁盘

    package com.zuiyu.common.license;import de.schlichtherle.license.AbstractKeyStoreParam;import java.io.*;/** * 自定义KeyStoreParam,用于将公私钥存储文件寄存到其余磁盘地位而不是我的项目中 */public class CustomKeyStoreParam extends AbstractKeyStoreParam {    /**     * 公钥/私钥在磁盘上的存储门路     */    private String storePath;    private String alias;    private String storePwd;    private String keyPwd;    public CustomKeyStoreParam(Class clazz, String resource, String alias, String storePwd, String keyPwd) {        super(clazz, resource);        this.storePath = resource;        this.alias = alias;        this.storePwd = storePwd;        this.keyPwd = keyPwd;    }    @Override    public String getAlias() {        return alias;    }    @Override    public String getStorePwd() {        return storePwd;    }    @Override    public String getKeyPwd() {        return keyPwd;    }    /**     * 复写de.schlichtherle.license.AbstractKeyStoreParam的getStream()办法<br/>     * 用于将公私钥存储文件寄存到其余磁盘地位而不是我的项目中     *     * @param     * @return java.io.InputStream     */    @Override    public InputStream getStream() throws IOException {        final InputStream in = new FileInputStream(new File(storePath));        if (null == in) {            throw new FileNotFoundException(storePath);        }        return in;    }}
  • 定义LicenseManagerHolder,获取全局的惟一一个受权治理对象

    public class LicenseManagerHolder {    private static volatile LicenseManager LICENSE_MANAGER;    public static LicenseManager getInstance(LicenseParam param) {        if (LICENSE_MANAGER == null) {            synchronized (LicenseManagerHolder.class) {                if (LICENSE_MANAGER == null) {                    LICENSE_MANAGER = new CustomLicenseManager(param);                }            }        }        return LICENSE_MANAGER;    }}
  • 定义受权文件校验类 LicenseVerify

    public class LicenseVerify {    public static Logger log = LoggerFactory.getLogger(LicenseVerify.class);    /**     * 装置License证书     */    public synchronized LicenseContent install(LicenseVerifyParam param) {        LicenseContent result = null;        DateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");        //1. 装置证书        try {            LicenseManager licenseManager = LicenseManagerHolder.getInstance(initLicenseParam(param));            licenseManager.uninstall();            result = licenseManager.install(new File(param.getLicensePath()));            log.info(MessageFormat.format("证书装置胜利,证书有效期:{0} - {1}", format.format(result.getNotBefore()), format.format(result.getNotAfter())));        } catch (Exception e) {            log.error("证书装置失败!", e);        }        return result;    }    /**     * 校验License证书     *     * @return boolean     */    public boolean verify() {        LicenseManager licenseManager = LicenseManagerHolder.getInstance(null);        DateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");        //2. 校验证书        try {            LicenseContent licenseContent = licenseManager.verify();//            System.out.println(licenseContent.getSubject());            log.info(MessageFormat.format("证书校验通过,证书有效期:{0} - {1}", format.format(licenseContent.getNotBefore()), format.format(licenseContent.getNotAfter())));            return true;        } catch (Exception e) {            log.error("证书校验失败!", e);            return false;        }    }    /**     * 初始化证书生成参数     *     * @param param License校验类须要的参数     * @return de.schlichtherle.license.LicenseParam     */    private LicenseParam initLicenseParam(LicenseVerifyParam param) {        Preferences preferences = Preferences.userNodeForPackage(LicenseVerify.class);        CipherParam cipherParam = new DefaultCipherParam(param.getStorePass());        KeyStoreParam publicStoreParam = new CustomKeyStoreParam(LicenseVerify.class                , param.getPublicKeysStorePath()                , param.getPublicAlias()                , param.getStorePass()                , null);        return new DefaultLicenseParam(param.getSubject()                , preferences                , publicStoreParam                , cipherParam);    }}
  • 定义受权文件生成辅助类 LicenseCreator

    public class LicenseCreator {    public static Logger log = LoggerFactory.getLogger(LicenseCreator.class);    private final static X500Principal DEFAULT_HOLDER_AND_ISSUER = new X500Principal("CN=localhost, OU=localhost, O=localhost, L=SH, ST=SH, C=CN");    private LicenseCreatorParam param;    public LicenseCreator(LicenseCreatorParam param) {        this.param = param;    }    /**     * 生成License证书     *     * @return boolean     */    public boolean generateLicense() {        try {            LicenseManager licenseManager = new CustomLicenseManager(initLicenseParam());            LicenseContent licenseContent = initLicenseContent();            licenseManager.store(licenseContent, new File(param.getLicensePath()));            return true;        } catch (Exception e) {            e.printStackTrace();            log.error(MessageFormat.format("证书生成失败:{0}", param), e.getMessage());            return false;        }    }    /**     * 初始化证书生成参数     *     * @return de.schlichtherle.license.LicenseParam     */    private LicenseParam initLicenseParam() {        Preferences preferences = Preferences.userNodeForPackage(LicenseCreator.class);        //设置对证书内容加密的秘钥        CipherParam cipherParam = new DefaultCipherParam(param.getStorePass());        KeyStoreParam privateStoreParam = new CustomKeyStoreParam(LicenseCreator.class                , param.getPrivateKeysStorePath()                , param.getPrivateAlias()                , param.getStorePass()                , param.getKeyPass());        LicenseParam licenseParam = new DefaultLicenseParam(param.getSubject()                , preferences                , privateStoreParam                , cipherParam);        return licenseParam;    }    /**     * 设置证书生成注释信息     *     * @return de.schlichtherle.license.LicenseContent     */    private LicenseContent initLicenseContent() {        LicenseContent licenseContent = new LicenseContent();        licenseContent.setHolder(DEFAULT_HOLDER_AND_ISSUER);        licenseContent.setIssuer(DEFAULT_HOLDER_AND_ISSUER);        licenseContent.setSubject(param.getSubject());        licenseContent.setIssued(param.getIssuedTime());        licenseContent.setNotBefore(param.getIssuedTime());        licenseContent.setNotAfter(param.getExpiryTime());        licenseContent.setConsumerType(param.getConsumerType());        licenseContent.setConsumerAmount(param.getConsumerAmount());        licenseContent.setInfo(param.getDescription());        //扩大校验服务器硬件信息        licenseContent.setExtra(param.getLicenseCheckModel());        return licenseContent;    }}

    能看到的这的是真爱吧,毕竟全是代码干货,没有一丁点的实践啊,这谁能hold住

    上面就能够进入common模块的打包了,打包完结就能够提供给server模块和client模块应用了

Maven common 模块打包

https://mp.weixin.qq.com/s?__...
  • license-common,批改pom.xml中的打包插件为上面这种
    <groupId>com.zuiyu</groupId>    <artifactId>license-common</artifactId>    <version>0.0.1-SNAPSHOT</version>    <name>license-common</name>    <build>        <plugins>            <plugin>                <groupId>org.apache.maven.plugins</groupId>                <artifactId>maven-compiler-plugin</artifactId>                <version>3.8.1</version>                <configuration>                    <source>1.8</source>                    <target>1.8</target>                    <encoding>UTF-8</encoding>                </configuration>            </plugin>        </plugins>    </build>
  • 执行idea中的打包,也就是mvn install

  • 须要援用该模块的代码我的项目pom.xml中退出以下内容

        <dependency>            <groupId>com.zuiyu</groupId>            <artifactId>license-common</artifactId>            <version>0.0.1-SNAPSHOT</version>            <scope>compile</scope>        </dependency>

    残缺的外围pom.xml文件如下,只保留打包相干

    • license-common

      <?xml version="1.0" encoding="UTF-8"?><project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">    <modelVersion>4.0.0</modelVersion>    <groupId>com.zuiyu</groupId>    <artifactId>license-common</artifactId>    <version>0.0.1-SNAPSHOT</version>    <name>license-common</name>    <description>license-client</description>    <build>        <plugins>            <plugin>                <groupId>org.apache.maven.plugins</groupId>                <artifactId>maven-compiler-plugin</artifactId>                <version>3.8.1</version>                <configuration>                    <source>1.8</source>                    <target>1.8</target>                    <encoding>UTF-8</encoding>                </configuration>            </plugin>        </plugins>    </build></project>

license-server

  • 引入license-common 模块

         <dependency>            <groupId>com.zuiyu</groupId>            <artifactId>license-common</artifactId>            <version>0.0.1-SNAPSHOT</version>            <scope>compile</scope>        </dependency>
  • 创立 LicenseCreatorController用于接口调用生成受权文件

    @RestController@RequestMapping("/license")public class LicenseCreatorController {    public static Logger log = LoggerFactory.getLogger(LicenseCreatorController.class);    /**     * 证书生成门路     */    @Value("${license.licensePath}")    private String licensePath;    @Value("${license.privateKeysStorePath}")    private String privateKeysStorePath;    /**     * 获取服务器硬件信息     *     * @param osName 操作系统类型,如果为空则主动判断     * @return com.ccx.models.license.LicenseCheckModel     */    @RequestMapping(value = "/getServerInfos", produces = {MediaType.APPLICATION_JSON_UTF8_VALUE})    public LicenseCheckModel getServerInfos(@RequestParam(value = "osName", required = false) String osName) {        //操作系统类型        if (StringUtils.isBlank(osName)) {            osName = System.getProperty("os.name");        }        osName = osName.toLowerCase();        AbstractServerInfos abstractServerInfos = null;        //依据不同操作系统类型抉择不同的数据获取办法        if (osName.startsWith("windows")) {            abstractServerInfos = new WindowsServerInfos();        } else if (osName.startsWith("linux")) {            abstractServerInfos = new LinuxServerInfos();        } else {//其余服务器类型            abstractServerInfos = new LinuxServerInfos();        }        return abstractServerInfos.getServerInfos();    }    /**     * 生成证书     *     * @param param 生成证书须要的参数,     *              {     *     "subject": "zuiyu_demo",     *     "privateAlias": "zuiyuPrivateKey",     *     "keyPass": "zuiyu_private_password_1234",     *     "storePass": "zuiyu_public_password_1234",     *     "licensePath": "/Users/cxt/Downloads/license/license7.lic",     *     "privateKeysStorePath": "/Users/cxt/Downloads/license/privateKeys.keystore",     *     "issuedTime": "2018-07-10 00:00:01",     *     "expiryTime": "2022-12-31 23:59:59",     *     "consumerType": "User",     *     "consumerAmount": 1,     *     "description": "这是证书形容信息",     *     "licenseCheckModel": {     *         "checkIp":false,     *         "ipAddress": [""],     *         "checkMac":true,     *         "macAddress": ["aa-bb-cc-11"],     *         "checkCpu":false,     *         "cpuSerial": "",     *         "checkMainBoard":false,     *         "mainBoardSerial": ""     *     }     * }     * @return java.util.Map<java.lang.String, java.lang.Object>     */    @RequestMapping(value = "/generateLicense", produces = {MediaType.APPLICATION_JSON_UTF8_VALUE})    public Map<String, Object> generateLicense(HttpServletRequest request, @RequestBody LicenseCreatorParam param) {        Map<String, Object> resultMap = new HashMap<>(2);        if (StringUtils.isBlank(param.getLicensePath())) {            param.setLicensePath(licensePath);        }        String path = System.getProperty("user.dir") + privateKeysStorePath;        param.setPrivateKeysStorePath(path);        LicenseCreator licenseCreator = new LicenseCreator(param);        boolean result = licenseCreator.generateLicense();        if (result) {            resultMap.put("result", "ok");            resultMap.put("msg", param);        } else {            resultMap.put("result", "error");            resultMap.put("msg", "证书文件生成失败!");        }        return resultMap;    }}
  • 找到方才生成的受权文件zuiyuPrivateKeys.keystore放入我的项目根目录license/zuiyuPrivateKeys.keystore
  • 批改application.properties

    # license 默认生成地址license.licensePath=/Users/cxt/Downloads/license/license.lic# 受权签名文件绝对我的项目的相对路径license.privateKeysStorePath=/springboot-license/license-server/license/zuiyuPrivateKeys.keystore
  • 残缺的我的项目构造如下

    ├── HELP.md├── README.md├── license│   └── zuiyuPrivateKeys.keystore├── pom.xml├── src│   ├── main│   │   ├── java│   │   │   └── com│   │   │       └── zuiyu│   │   │           └── server│   │   │               ├── LicenseServerApplication.java│   │   │               └── controller│   │   │                   └── LicenseCreatorController.java│   │   └── resources│   │       └── application.properties
  • 申请接口生成受权

    Post localhost:8080/license/generateLicense{    "subject": "zuiyu_demo",    "privateAlias": "zuiyuPrivateKey",    "keyPass": "zuiyu_private_password_1234",    "storePass": "zuiyu_public_password_1234",    "licensePath": "/Users/cxt/Downloads/license/license7.lic",    "privateKeysStorePath": "/Users/cxt/Downloads/license/privateKeys.keystore",    "issuedTime": "2018-07-10 00:00:01",    "expiryTime": "2022-12-31 23:59:59",    "consumerType": "User",    "consumerAmount": 1,    "description": "这是证书形容信息",    "licenseCheckModel": {        "checkIp":false,        "ipAddress": [""],        "checkMac":true,        "macAddress": ["aa-bb-cc-11"],        "checkCpu":false,        "cpuSerial": "",        "checkMainBoard":false,        "mainBoardSerial": ""    }}

license-client

  • 引入license-common 模块

         <dependency>            <groupId>com.zuiyu</groupId>            <artifactId>license-common</artifactId>            <version>0.0.1-SNAPSHOT</version>            <scope>compile</scope>        </dependency>
  • 注册受权验证拦截器InterceptorConfig

    @Componentpublic class InterceptorConfig implements WebMvcConfigurer {    @Resource    private LicenseCheckInterceptor licenseCheckInterceptor;    @Override    public void addInterceptors(InterceptorRegistry registry) {        // 增加要拦挡的url        registry.addInterceptor(licenseCheckInterceptor)                // 拦挡的门路                .addPathPatterns("/**");        // 放行的门路//                .excludePathPatterns("/admin/login");    }}
  • 定义拦截器拦挡申请验证受权 LicenseCheckInterceptor

    @Componentpublic class LicenseCheckInterceptor implements HandlerInterceptor {    public static Logger log = LoggerFactory.getLogger(LicenseCheckInterceptor.class);    /**     * 进入controller层之前拦挡申请     *     * @param request     * @param response     * @param handler     * @return     * @throws Exception     */    @Override    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {        log.info("进入拦截器,验证证书可应用性");        LicenseVerify licenseVerify = new LicenseVerify();        //校验证书是否无效        boolean verifyResult = licenseVerify.verify();        if (verifyResult) {            log.info("验证胜利,证书可用");            return true;        } else {            log.info("验证失败,证书有效");            response.setCharacterEncoding("utf-8");            Map<String, String> result = new HashMap<>(1);            result.put("result", "您的证书有效,请核查服务器是否获得受权或从新申请证书!");            response.getWriter().write(JSON.toJSONString(result));            return false;        }    }    /**     * 解决申请实现后视图渲染之前的解决操作     *     * @param request     * @param response     * @param handler     * @param modelAndView     * @throws Exception     */    @Override    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable ModelAndView modelAndView) throws Exception {        log.info("解决申请实现后视图渲染之前的解决操作");    }    /**     * 视图渲染之后的操作     *     * @param request     * @param response     * @param handler     * @param ex     * @throws Exception     */    @Override    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable Exception ex) throws Exception {        log.info("视图渲染之后的操作");    }}
  • 定义启动利用时查看受权LicenseCheckListener

    @Componentpublic class LicenseCheckListener implements ApplicationListener<ContextRefreshedEvent> {    public static Logger log = LoggerFactory.getLogger(LicenseCheckListener.class);    /**     * 证书subject     */    @Value("${license.subject}")    private String subject;    /**     * 公钥别称     */    @Value("${license.publicAlias}")    private String publicAlias;    /**     * 拜访公钥库的明码     */    @Value("${license.storePass}")    private String storePass;    /**     * 证书生成门路     */    @Value("${license.licensePath}")    private String licensePath;    /**     * 密钥库存储门路     */    @Value("${license.publicKeysStorePath}")    private String publicKeysStorePath;    @Override    public void onApplicationEvent(ContextRefreshedEvent event) {        // root application context 没有parent        ApplicationContext context = event.getApplicationContext().getParent();        if (context == null) {            if (StringUtils.isNotBlank(licensePath)) {                log.info("++++++++ 开始装置证书 ++++++++");                LicenseVerifyParam param = new LicenseVerifyParam();                param.setSubject(subject);                param.setPublicAlias(publicAlias);                param.setStorePass(storePass);                param.setLicensePath(licensePath);                param.setPublicKeysStorePath(System.getProperty("user.dir") + publicKeysStorePath);                LicenseVerify licenseVerify = new LicenseVerify();                //装置证书                licenseVerify.install(param);                log.info("++++++++ 证书装置完结 ++++++++");            }        }    }}
  • 新建一个测试类,测试受权是否失效LoginController

    public class LoginController {    public static Logger log = LoggerFactory.getLogger(LoginController.class);    @GetMapping(value = "login")    public Map<String, Object> test(@RequestParam(required = true) String loginName, @RequestParam(required = true) String password) {        Map<String, Object> result = new HashMap<>(1);        log.info(MessageFormat.format("登录名称:{0},明码:{1}", loginName, password));        //模仿登录        log.info("模仿登录被拦挡查看证书可用性");        result.put("code", 200);        return result;    }}
  • 测试接口调用

    get localhost:8082/login?loginName=111&password=111
  • 残缺代码构造如下

    ├── HELP.md├── README.md├── license│   └── zuiyuPublicCerts.keystore├── pom.xml├── src│   ├── main│   │   ├── java│   │   │   └── com│   │   │       └── zuiyu│   │   │           └── client│   │   │               ├── LicenseClientApplication.java│   │   │               ├── config│   │   │               │   └── InterceptorConfig.java│   │   │               ├── controller│   │   │               │   └── LoginController.java│   │   │               ├── interceptor│   │   │               │   └── LicenseCheckInterceptor.java│   │   │               └── license│   │   │                   └── LicenseCheckListener.java│   │   └── resources│   │       └── application.properties

本文由mdnice多平台公布