Java:项目license证书控制

学习记录……

原文:Spring Boot项目中使用 TrueLicense 生成和验 | zifangsky的个人博客

Java:项目license证书控制_LLLLLiSHI的博客-CSDN博客_java license

license即版权许可证。实际使用中部署给客户的项目,不能随意被二次使用。那么就需要license证书对项目做出控制。

1 pom:

 
  1. <!-- license处理 -->

  2. <dependency>

  3. <groupId>de.schlichtherle.truelicense</groupId>

  4. <artifactId>truelicense-core</artifactId>

  5. <version>1.33</version>

  6. <scope>compile</scope>

  7. </dependency>

2

服务端:生成license

客户端:对license做出验证

3 具体步骤

1、创建一个服务端项目和一个客户端项目
2、导入相关依赖及相关类
3、执行keytool命令,生成密钥库文件
4、调用服务端接口,以密钥库生成license文件
5、指定项目配置(如license路径),启动客户端
6、完

附1:服务端提供API
 

 
  1. /**

  2. * 用于生成证书文件,不能放在给客户部署的代码里

  3. *

  4. */

  5. @Api(tags = "license api")

  6. @RestController

  7. @RequestMapping("/license")

  8. public class LicenseApi {

  9. /**

  10. * 证书生成路径

  11. */

  12. @Value("${license.licensePath}")

  13. private String licensePath;

  14. /**

  15. * 获取服务器硬件信息

  16. */

  17. @ApiOperation(value = "获取服务器硬件信息")

  18. @GetMapping(value = "/serverInfo")

  19. public ResponseResult<LicenseCheckModel> getServerInfos(@RequestParam(value = "osName", required = false) String osName) {

  20. //操作系统类型

  21. if (StrUtil.isBlank(osName)) {

  22. osName = System.getProperty("os.name");

  23. }

  24. osName = osName.toLowerCase();

  25. AbstractServerInfos abstractServerInfos = null;

  26. //根据不同操作系统类型选择不同的数据获取方法

  27. if (osName.startsWith("windows")) {

  28. abstractServerInfos = new WindowsServerInfos();

  29. } else {

  30. //其他服务器类型

  31. abstractServerInfos = new LinuxServerInfos();

  32. }

  33. return ResponseResult.ok(abstractServerInfos.getServerInfos());

  34. }

  35. /**

  36. * 生成证书

  37. */

  38. @ApiOperation(value = "生成证书")

  39. @PostMapping(value = "/generateLicense")

  40. public ResponseResult generateLicense(@RequestBody(required = true) LicenseCreatorParam param) {

  41. if (StrUtil.isBlank(param.getLicensePath())) {

  42. param.setLicensePath(licensePath);

  43. }

  44. LicenseCreator licenseCreator = new LicenseCreator(param);

  45. boolean result = licenseCreator.generateLicense();

  46. if (result) {

  47. return ResponseResult.ok("证书文件生成成功");

  48. } else {

  49. return ResponseResult.be("证书文件生成失败");

  50. }

  51. }

  52. }

生成证书示例

 
  1. {

  2. "subject": "custom_license",

  3. "privateAlias": "privateKey",

  4. "keyPass": "private_password1234",

  5. "storePass": "public_password1234",

  6. "licensePath": "D:/temp/keytool/license.lic",

  7. "privateKeysStorePath": "D:/temp/keytool/privateKeys.keystore",

  8. "issuedTime": "2020-06-03 00:00:01",

  9. "expiryTime": "2020-06-03 23:59:59",

  10. "consumerType": "User",

  11. "consumerAmount": 1,

  12. "description": "这是证书描述信息",

  13. "licenseCheckModel": {

  14. "ipAddress": [

  15. "1.1.1.1"

  16. ],

  17. "macAddress": [

  18. "A#-D5-D3-12-1F-D2"

  19. ],

  20. "cpuSerial": "ADADADADA000239",

  21. "mainBoardSerial": "199023140001"

  22. }

  23. }

附2:keytool命令

 
  1. ### license生成证书命令,JDK自带keytool工具

  2. #生成:在当前目录下,生成一个名为privateKeys.keystore的密钥库,同时指定密钥库密码为public_password1234,第一个条目为privateKey,指定条目密码为private_password1234

  3. keytool -genkeypair -keysize 1024 -validity 1 -alias "privateKey" -keystore "privateKeys.keystore" -storepass "public_password1234" -keypass "private_password1234" -dname "CN=localhost, OU=localhost, O=localhost, L=SH, ST=SH, C=CN"

  4. #导出:将密钥库中privateKey的条目,导出到一个名为certfile.cer的数字文件

  5. keytool -exportcert -alias "privateKey" -keystore "privateKeys.keystore" -storepass "public_password1234" -file "certfile.cer"

  6. #导入:将刚刚导出的数字文件,导入为一个新的密钥库publicCerts.keystore

  7. keytool -import -alias "publicCert" -file "certfile.cer" -keystore "publicCerts.keystore" -storepass "public_password1234"

  8. 参数释义:

  9. alias:第一个条目别名

  10. validity:证书有效期,单位:天

  11. keystore:指定密钥库名称

  12. storepass:指定密钥库密码

  13. keypass:指定别名对应密码(私钥密码)

  14. 此三命令执行完之后,会生成三个文件:privateKeys.keystore,publicCerts.keystore,certfile.cer。

  15. privateKeys.keystore:用于服务端生产license文件

  16. publicCerts.keystore:随客户端项目部署

  17. certfile.cer:暂存文件,删除即可

附3:代码

先说明服务端和客户端都需要通用的业务类:

AbstractServerInfos

 
  1. /**

  2. * 用于获取客户服务器的基本信息,如:IP、Mac地址、CPU序列号、主板序列号等

  3. *

  4. */

  5. @Data

  6. @Slf4j

  7. public abstract class AbstractServerInfos {

  8. /**

  9. * 组装需要额外校验的License参数

  10. */

  11. public LicenseCheckModel getServerInfos() {

  12. LicenseCheckModel result = new LicenseCheckModel();

  13. try {

  14. result.setIpAddress(this.getIpAddress());

  15. result.setMacAddress(this.getMacAddress());

  16. result.setCpuSerial(this.getCPUSerial());

  17. result.setMainBoardSerial(this.getMainBoardSerial());

  18. } catch (Exception e) {

  19. log.error("获取服务器硬件信息失败", e);

  20. }

  21. return result;

  22. }

  23. /**

  24. * 获取IP地址

  25. */

  26. protected abstract List<String> getIpAddress() throws Exception;

  27. /**

  28. * 获取Mac地址

  29. */

  30. protected abstract List<String> getMacAddress() throws Exception;

  31. /**

  32. * 获取CPU序列号

  33. */

  34. protected abstract String getCPUSerial() throws Exception;

  35. /**

  36. * 获取主板序列号

  37. */

  38. protected abstract String getMainBoardSerial() throws Exception;

  39. /**

  40. * 获取某个网络接口的Mac地址

  41. */

  42. protected String getMacByInetAddress(InetAddress inetAddr) {

  43. try {

  44. byte[] mac = NetworkInterface.getByInetAddress(inetAddr).getHardwareAddress();

  45. StringBuffer stringBuffer = new StringBuffer();

  46. for (int i = 0; i < mac.length; i++) {

  47. if (i != 0) {

  48. stringBuffer.append("-");

  49. }

  50. //将十六进制byte转化为字符串

  51. String temp = Integer.toHexString(mac[i] & 0xff);

  52. if (temp.length() == 1) {

  53. stringBuffer.append("0" + temp);

  54. } else {

  55. stringBuffer.append(temp);

  56. }

  57. }

  58. return stringBuffer.toString().toUpperCase();

  59. } catch (SocketException e) {

  60. log.info("net:{}", JSON.toJSONString(inetAddr));

  61. log.error("获取网络接口的Mac地址", e);

  62. }

  63. return null;

  64. }

  65. /**

  66. * 获取当前服务器所有符合条件的InetAddress

  67. */

  68. protected List<InetAddress> getLocalAllInetAddress() throws Exception {

  69. List<InetAddress> result = new ArrayList<>(4);

  70. // 遍历所有的网络接口

  71. for (Enumeration networkInterfaces = NetworkInterface.getNetworkInterfaces(); networkInterfaces.hasMoreElements(); ) {

  72. NetworkInterface iface = (NetworkInterface) networkInterfaces.nextElement();

  73. // 在所有的接口下再遍历IP

  74. for (Enumeration inetAddresses = iface.getInetAddresses(); inetAddresses.hasMoreElements(); ) {

  75. InetAddress inetAddr = (InetAddress) inetAddresses.nextElement();

  76. //排除LoopbackAddress、SiteLocalAddress、LinkLocalAddress、MulticastAddress类型的IP地址

  77. /*&& !inetAddr.isSiteLocalAddress()*/

  78. if (!inetAddr.isLoopbackAddress()

  79. && !inetAddr.isLinkLocalAddress()

  80. && !inetAddr.isMulticastAddress()) {

  81. result.add(inetAddr);

  82. }

  83. }

  84. }

  85. return result;

  86. }

  87. }

CustomKeyStoreParam

 
  1. /**

  2. * 自定义KeyStoreParam,用于将公私钥存储文件存放到其他磁盘位置而不是项目中

  3. *

  4. */

  5. public class CustomKeyStoreParam extends AbstractKeyStoreParam {

  6. /**

  7. * 公钥/私钥在磁盘上的存储路径

  8. */

  9. private String storePath;

  10. private String alias;

  11. private String storePwd;

  12. private String keyPwd;

  13. public CustomKeyStoreParam(Class clazz, String resource, String alias, String storePwd, String keyPwd) {

  14. super(clazz, resource);

  15. this.storePath = resource;

  16. this.alias = alias;

  17. this.storePwd = storePwd;

  18. this.keyPwd = keyPwd;

  19. }

  20. @Override

  21. public String getAlias() {

  22. return alias;

  23. }

  24. @Override

  25. public String getStorePwd() {

  26. return storePwd;

  27. }

  28. @Override

  29. public String getKeyPwd() {

  30. return keyPwd;

  31. }

  32. /**

  33. * 复写de.schlichtherle.license.AbstractKeyStoreParam的getStream()方法

  34. * <br/>

  35. * 用于将公私钥存储文件存放到其他磁盘位置而不是项目中

  36. */

  37. @Override

  38. public InputStream getStream() throws IOException {

  39. final InputStream in = new FileInputStream(new File(storePath));

  40. if (null == in) {

  41. throw new FileNotFoundException(storePath);

  42. }

  43. return in;

  44. }

  45. }

CustomLicenseManager

 
  1. /**

  2. * 自定义LicenseManager,用于增加额外的服务器硬件信息校验

  3. *

  4. */

  5. @Slf4j

  6. public class CustomLicenseManager extends LicenseManager {

  7. /**

  8. * XML编码

  9. */

  10. private static final String XML_CHARSET = "UTF-8";

  11. /**

  12. * 默认 buf size

  13. */

  14. private static final int DEFAULT_BUFSIZE = 8 * 1024;

  15. public CustomLicenseManager() {

  16. }

  17. public CustomLicenseManager(LicenseParam param) {

  18. super(param);

  19. }

  20. /**

  21. * 复写create方法

  22. */

  23. @Override

  24. protected synchronized byte[] create(LicenseContent content, LicenseNotary notary) throws Exception {

  25. initialize(content);

  26. this.validateCreate(content);

  27. final GenericCertificate certificate = notary.sign(content);

  28. return getPrivacyGuard().cert2key(certificate);

  29. }

  30. /**

  31. * 复写install方法,其中validate方法调用本类中的validate方法,校验IP地址、Mac地址等其他信息

  32. */

  33. @Override

  34. protected synchronized LicenseContent install(final byte[] key, final LicenseNotary notary) throws Exception {

  35. final GenericCertificate certificate = getPrivacyGuard().key2cert(key);

  36. notary.verify(certificate);

  37. final LicenseContent content = (LicenseContent) this.load(certificate.getEncoded());

  38. this.validate(content);

  39. setLicenseKey(key);

  40. setCertificate(certificate);

  41. return content;

  42. }

  43. /**

  44. * 复写verify方法,调用本类中的validate方法,校验IP地址、Mac地址等其他信息

  45. */

  46. @Override

  47. protected synchronized LicenseContent verify(final LicenseNotary notary) throws Exception {

  48. GenericCertificate certificate = getCertificate();

  49. // Load license key from preferences,

  50. final byte[] key = getLicenseKey();

  51. if (null == key) {

  52. throw new NoLicenseInstalledException(getLicenseParam().getSubject());

  53. }

  54. certificate = getPrivacyGuard().key2cert(key);

  55. notary.verify(certificate);

  56. final LicenseContent content = (LicenseContent) this.load(certificate.getEncoded());

  57. this.validate(content);

  58. setCertificate(certificate);

  59. return content;

  60. }

  61. /**

  62. * 校验生成证书的参数信息

  63. */

  64. protected synchronized void validateCreate(final LicenseContent content) throws LicenseContentException {

  65. final LicenseParam param = getLicenseParam();

  66. final Date now = new Date();

  67. final Date notBefore = content.getNotBefore();

  68. final Date notAfter = content.getNotAfter();

  69. if (null != notAfter && now.after(notAfter)) {

  70. throw new LicenseContentException("证书失效时间不能早于当前时间");

  71. }

  72. if (null != notBefore && null != notAfter && notAfter.before(notBefore)) {

  73. throw new LicenseContentException("证书生效时间不能晚于证书失效时间");

  74. }

  75. final String consumerType = content.getConsumerType();

  76. if (null == consumerType) {

  77. throw new LicenseContentException("用户类型不能为空");

  78. }

  79. }

  80. /**

  81. * 复写validate方法,增加IP地址、Mac地址等其他信息校验

  82. */

  83. @Override

  84. protected synchronized void validate(final LicenseContent content) throws LicenseContentException {

  85. //1. 首先调用父类的validate方法

  86. super.validate(content);

  87. //2. 然后校验自定义的License参数

  88. //License中可被允许的参数信息

  89. LicenseCheckModel expectedCheckModel = (LicenseCheckModel) content.getExtra();

  90. //当前服务器真实的参数信息

  91. LicenseCheckModel serverCheckModel = getServerInfos();

  92. if (expectedCheckModel != null && serverCheckModel != null) {

  93. //校验主板序列号

  94. if (!checkSerial(expectedCheckModel.getMainBoardSerial(), serverCheckModel.getMainBoardSerial())) {

  95. throw new LicenseContentException("当前服务器的主板序列号没在授权范围内");

  96. }

  97. //校验IP地址

  98. if (!checkIpAddress(expectedCheckModel.getIpAddress(), serverCheckModel.getIpAddress())) {

  99. throw new LicenseContentException("当前服务器的IP没在授权范围内");

  100. }

  101. //校验Mac地址

  102. if (!checkIpAddress(expectedCheckModel.getMacAddress(), serverCheckModel.getMacAddress())) {

  103. throw new LicenseContentException("当前服务器的Mac地址没在授权范围内");

  104. }

  105. //校验CPU序列号

  106. if (!checkSerial(expectedCheckModel.getCpuSerial(), serverCheckModel.getCpuSerial())) {

  107. throw new LicenseContentException("当前服务器的CPU序列号没在授权范围内");

  108. }

  109. } else {

  110. throw new LicenseContentException("不能获取服务器硬件信息");

  111. }

  112. }

  113. /**

  114. * 重写XMLDecoder解析XML

  115. */

  116. private Object load(String encoded) {

  117. BufferedInputStream inputStream = null;

  118. XMLDecoder decoder = null;

  119. try {

  120. // 替换自定义license参数类:以客户端定义的LicenseCheckModel,替换xml中服务端的LicenseCheckModel路径

  121. String clazzName = LicenseCheckModel.class.getName();

  122. encoded = encoded.replace("cn.test.license.LicenseCheckModel", clazzName);

  123. inputStream = new BufferedInputStream(new ByteArrayInputStream(encoded.getBytes(XML_CHARSET)));

  124. decoder = new XMLDecoder(new BufferedInputStream(inputStream, DEFAULT_BUFSIZE), null, null);

  125. return decoder.readObject();

  126. } catch (UnsupportedEncodingException e) {

  127. log.error("解析XML异常", e);

  128. } finally {

  129. try {

  130. if (decoder != null) {

  131. decoder.close();

  132. }

  133. if (inputStream != null) {

  134. inputStream.close();

  135. }

  136. } catch (Exception e) {

  137. log.error("XMLDecoder解析XML失败", e);

  138. }

  139. }

  140. return null;

  141. }

  142. /**

  143. * 获取当前服务器需要额外校验的License参数

  144. */

  145. private LicenseCheckModel getServerInfos() {

  146. //操作系统类型

  147. String osName = System.getProperty("os.name").toLowerCase();

  148. AbstractServerInfos abstractServerInfos = null;

  149. //根据不同操作系统类型选择不同的数据获取方法

  150. if (osName.startsWith("windows")) {

  151. abstractServerInfos = new WindowsServerInfos();

  152. } else if (osName.startsWith("linux")) {

  153. abstractServerInfos = new LinuxServerInfos();

  154. } else {

  155. //其他服务器类型

  156. abstractServerInfos = new LinuxServerInfos();

  157. }

  158. return abstractServerInfos.getServerInfos();

  159. }

  160. /**

  161. * 校验当前服务器的IP/Mac地址是否在可被允许的IP范围内

  162. * <br/>

  163. * 如果存在IP在可被允许的IP/Mac地址范围内,则返回true

  164. */

  165. private boolean checkIpAddress(List<String> expectedList, List<String> serverList) {

  166. if (expectedList != null && expectedList.size() > 0) {

  167. if (serverList != null && serverList.size() > 0) {

  168. for (String expected : expectedList) {

  169. if (serverList.contains(expected.trim())) {

  170. return true;

  171. }

  172. }

  173. }

  174. return false;

  175. } else {

  176. return true;

  177. }

  178. }

  179. /**

  180. * 校验当前服务器硬件(主板、CPU等)序列号是否在可允许范围内

  181. */

  182. private boolean checkSerial(String expectedSerial, String serverSerial) {

  183. if (StrUtil.isNotBlank(expectedSerial)) {

  184. if (StrUtil.isNotBlank(serverSerial)) {

  185. return expectedSerial.equals(serverSerial);

  186. }

  187. return false;

  188. } else {

  189. return true;

  190. }

  191. }

  192. }

LicenseCheckModel

 
  1. /**

  2. * 自定义需要校验的License参数

  3. *

  4. */

  5. @Data

  6. @ApiModel("服务器校验信息")

  7. public class LicenseCheckModel implements Serializable {

  8. /**

  9. * 可被允许的IP地址

  10. */

  11. @ApiModelProperty("可被允许的IP地址")

  12. private List<String> ipAddress;

  13. /**

  14. * 可被允许的MAC地址

  15. */

  16. @ApiModelProperty("可被允许的MAC地址")

  17. private List<String> macAddress;

  18. /**

  19. * 可被允许的CPU序列号

  20. */

  21. @ApiModelProperty("可被允许的CPU序列号")

  22. private String cpuSerial;

  23. /**

  24. * 可被允许的主板序列号

  25. */

  26. @ApiModelProperty("可被允许的主板序列号")

  27. private String mainBoardSerial;

  28. }

LinuxServerInfos

 
  1. /**

  2. * 用于获取客户Linux服务器的基本信息

  3. *

  4. */

  5. public class LinuxServerInfos extends AbstractServerInfos {

  6. @Override

  7. protected List<String> getIpAddress() throws Exception {

  8. List<String> result = null;

  9. //获取所有网络接口

  10. List<InetAddress> inetAddresses = getLocalAllInetAddress();

  11. if (inetAddresses != null && inetAddresses.size() > 0) {

  12. result = inetAddresses.stream().map(InetAddress::getHostAddress).distinct().map(String::toLowerCase).collect(Collectors.toList());

  13. }

  14. return result;

  15. }

  16. @Override

  17. protected List<String> getMacAddress() throws Exception {

  18. List<String> result = null;

  19. //1. 获取所有网络接口

  20. List<InetAddress> inetAddresses = getLocalAllInetAddress();

  21. if (inetAddresses != null && inetAddresses.size() > 0) {

  22. //2. 获取所有网络接口的Mac地址

  23. result = inetAddresses.stream().map(this::getMacByInetAddress).distinct().collect(Collectors.toList());

  24. }

  25. return result;

  26. }

  27. @Override

  28. protected String getCPUSerial() throws Exception {

  29. //序列号

  30. String serialNumber = "";

  31. //使用dmidecode命令获取CPU序列号

  32. String[] shell = {"/bin/bash", "-c", "dmidecode -t processor | grep 'ID' | awk -F ':' '{print $2}' | head -n 1"};

  33. Process process = Runtime.getRuntime().exec(shell);

  34. process.getOutputStream().close();

  35. BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()));

  36. String line = reader.readLine().trim();

  37. if (StrUtil.isNotBlank(line)) {

  38. serialNumber = line;

  39. }

  40. reader.close();

  41. return serialNumber;

  42. }

  43. @Override

  44. protected String getMainBoardSerial() throws Exception {

  45. //序列号

  46. String serialNumber = "";

  47. //使用dmidecode命令获取主板序列号

  48. String[] shell = {"/bin/bash", "-c", "dmidecode | grep 'Serial Number' | awk -F ':' '{print $2}' | head -n 1"};

  49. Process process = Runtime.getRuntime().exec(shell);

  50. process.getOutputStream().close();

  51. BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()));

  52. String line = reader.readLine().trim();

  53. if (StrUtil.isNotBlank(line)) {

  54. serialNumber = line;

  55. }

  56. reader.close();

  57. return serialNumber;

  58. }

  59. }

WindowsServerInfos

 
  1. /**

  2. * 用于获取客户Windows服务器的基本信息

  3. *

  4. */

  5. public class WindowsServerInfos extends AbstractServerInfos {

  6. @Override

  7. protected List<String> getIpAddress() throws Exception {

  8. List<String> result = null;

  9. //获取所有网络接口

  10. List<InetAddress> inetAddresses = getLocalAllInetAddress();

  11. if (inetAddresses != null && inetAddresses.size() > 0) {

  12. result = inetAddresses.stream().map(InetAddress::getHostAddress).distinct().map(String::toLowerCase).collect(Collectors.toList());

  13. }

  14. return result;

  15. }

  16. @Override

  17. protected List<String> getMacAddress() throws Exception {

  18. List<String> result = null;

  19. //1. 获取所有网络接口

  20. List<InetAddress> inetAddresses = getLocalAllInetAddress();

  21. if (inetAddresses != null && inetAddresses.size() > 0) {

  22. //2. 获取所有网络接口的Mac地址

  23. result = inetAddresses.stream().map(this::getMacByInetAddress).distinct().collect(Collectors.toList());

  24. }

  25. return result;

  26. }

  27. @Override

  28. protected String getCPUSerial() throws Exception {

  29. //序列号

  30. String serialNumber = "";

  31. //使用WMIC获取CPU序列号

  32. Process process = Runtime.getRuntime().exec("wmic cpu get processorid");

  33. process.getOutputStream().close();

  34. Scanner scanner = new Scanner(process.getInputStream());

  35. if (scanner.hasNext()) {

  36. scanner.next();

  37. }

  38. if (scanner.hasNext()) {

  39. serialNumber = scanner.next().trim();

  40. }

  41. scanner.close();

  42. return serialNumber;

  43. }

  44. @Override

  45. protected String getMainBoardSerial() throws Exception {

  46. //序列号

  47. String serialNumber = "";

  48. //使用WMIC获取主板序列号

  49. Process process = Runtime.getRuntime().exec("wmic baseboard get serialnumber");

  50. process.getOutputStream().close();

  51. Scanner scanner = new Scanner(process.getInputStream());

  52. if (scanner.hasNext()) {

  53. scanner.next();

  54. }

  55. if (scanner.hasNext()) {

  56. serialNumber = scanner.next().trim();

  57. }

  58. scanner.close();

  59. return serialNumber;

  60. }

  61. }

服务端

yml

 
  1. #License相关配置

  2. license:

  3. licensePath: license/license.lic

LicenseCreator

 
  1. /**

  2. * License生成类

  3. */

  4. @Slf4j

  5. public class LicenseCreator {

  6. private final static X500Principal DEFAULT_HOLDER_AND_ISSUER = new X500Principal("CN=localhost, OU=localhost, O=localhost, L=SH, ST=SH, C=CN");

  7. private LicenseCreatorParam param;

  8. public LicenseCreator(LicenseCreatorParam param) {

  9. this.param = param;

  10. }

  11. /**

  12. * 生成License证书

  13. */

  14. public boolean generateLicense() {

  15. try {

  16. LicenseManager licenseManager = new CustomLicenseManager(initLicenseParam());

  17. LicenseContent licenseContent = initLicenseContent();

  18. licenseManager.store(licenseContent, new File(param.getLicensePath()));

  19. return true;

  20. } catch (Exception e) {

  21. log.error(MessageFormat.format("证书生成失败:{0}", param), e);

  22. return false;

  23. }

  24. }

  25. /**

  26. * 初始化证书生成参数

  27. */

  28. private LicenseParam initLicenseParam() {

  29. Preferences preferences = Preferences.userNodeForPackage(LicenseCreator.class);

  30. //设置对证书内容加密的秘钥

  31. CipherParam cipherParam = new DefaultCipherParam(param.getStorePass());

  32. KeyStoreParam privateStoreParam = new CustomKeyStoreParam(LicenseCreator.class

  33. , param.getPrivateKeysStorePath()

  34. , param.getPrivateAlias()

  35. , param.getStorePass()

  36. , param.getKeyPass());

  37. LicenseParam licenseParam = new DefaultLicenseParam(param.getSubject()

  38. , preferences

  39. , privateStoreParam

  40. , cipherParam);

  41. return licenseParam;

  42. }

  43. /**

  44. * 设置证书生成正文信息

  45. */

  46. private LicenseContent initLicenseContent() {

  47. LicenseContent licenseContent = new LicenseContent();

  48. licenseContent.setHolder(DEFAULT_HOLDER_AND_ISSUER);

  49. licenseContent.setIssuer(DEFAULT_HOLDER_AND_ISSUER);

  50. licenseContent.setSubject(param.getSubject());

  51. licenseContent.setIssued(param.getIssuedTime());

  52. licenseContent.setNotBefore(param.getIssuedTime());

  53. licenseContent.setNotAfter(param.getExpiryTime());

  54. licenseContent.setConsumerType(param.getConsumerType());

  55. licenseContent.setConsumerAmount(param.getConsumerAmount());

  56. licenseContent.setInfo(param.getDescription());

  57. //扩展校验服务器硬件信息

  58. licenseContent.setExtra(param.getLicenseCheckModel());

  59. return licenseContent;

  60. }

  61. }

LicenseCreatorParam

 
  1. /**

  2. * License生成类需要的参数

  3. *

  4. */

  5. @Data

  6. @ApiModel("License需要的参数")

  7. public class LicenseCreatorParam implements Serializable {

  8. /**

  9. * 证书subject

  10. */

  11. @ApiModelProperty("证书subject")

  12. private String subject;

  13. /**

  14. * 密钥别称

  15. */

  16. @ApiModelProperty("密钥别称")

  17. private String privateAlias;

  18. /**

  19. * 密钥密码(需要妥善保管,不能让使用者知道)

  20. */

  21. @ApiModelProperty("密钥密码")

  22. private String keyPass;

  23. /**

  24. * 访问秘钥库的密码

  25. */

  26. @ApiModelProperty("访问秘钥库的密码")

  27. private String storePass;

  28. /**

  29. * 证书生成路径

  30. */

  31. @ApiModelProperty("证书生成路径")

  32. private String licensePath;

  33. /**

  34. * 密钥库存储路径

  35. */

  36. @ApiModelProperty("密钥库存储路径")

  37. private String privateKeysStorePath;

  38. /**

  39. * 证书生效时间

  40. */

  41. @ApiModelProperty("证书生效时间")

  42. @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")

  43. private Date issuedTime = new Date();

  44. /**

  45. * 证书失效时间

  46. */

  47. @ApiModelProperty("证书失效时间")

  48. @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")

  49. private Date expiryTime;

  50. /**

  51. * 用户类型

  52. */

  53. @ApiModelProperty("用户类型")

  54. private String consumerType = "User";

  55. /**

  56. * 用户数量

  57. */

  58. @ApiModelProperty("用户数量")

  59. private Integer consumerAmount = 1;

  60. /**

  61. * 描述信息

  62. */

  63. @ApiModelProperty("描述信息")

  64. private String description = "";

  65. /**

  66. * 额外的服务器硬件校验信息

  67. */

  68. @ApiModelProperty("服务器校验信息")

  69. private LicenseCheckModel licenseCheckModel;

  70. }

客户端

yml:

 
  1. ##License相关配置

  2. license:

  3. subject: custom_license

  4. publicAlias: publicCert

  5. storePass: 1234567890asdfghjkl

  6. licensePath: D:\temp\keytool\license.lic

  7. publicKeysStorePath: D:\temp\keytool\publicCerts.keystore

LicenseManagerHolder

 
  1. /**

  2. * de.schlichtherle.license.LicenseManager的单例

  3. *

  4. */

  5. public class LicenseManagerHolder {

  6. private static volatile LicenseManager LICENSE_MANAGER;

  7. public static LicenseManager getInstance(LicenseParam param) {

  8. if (LICENSE_MANAGER == null) {

  9. synchronized (LicenseManagerHolder.class) {

  10. if (LICENSE_MANAGER == null) {

  11. LICENSE_MANAGER = new CustomLicenseManager(param);

  12. }

  13. }

  14. }

  15. return LICENSE_MANAGER;

  16. }

  17. }

LicenseVerify

 
  1. /**

  2. * License校验类

  3. *

  4. */

  5. @Slf4j

  6. public class LicenseVerify {

  7. /**

  8. * 安装License证书

  9. */

  10. public synchronized LicenseContent install(LicenseVerifyParam param) {

  11. LicenseContent result = null;

  12. //1. 安装证书

  13. try {

  14. LicenseManager licenseManager = LicenseManagerHolder.getInstance(initLicenseParam(param));

  15. licenseManager.uninstall();

  16. result = licenseManager.install(new File(param.getLicensePath()));

  17. log.info("证书安装成功,证书有效期:{} - {}",

  18. DateUtil.formatDateTime(result.getNotBefore()),

  19. DateUtil.formatDateTime(result.getNotAfter()));

  20. } catch (Exception e) {

  21. log.error("证书安装失败!", e);

  22. throw new BusinessException("证书安装失败");

  23. }

  24. return result;

  25. }

  26. /**

  27. * 校验License证书

  28. */

  29. public boolean verify() {

  30. LicenseManager licenseManager = LicenseManagerHolder.getInstance(null);

  31. //2. 校验证书

  32. try {

  33. LicenseContent licenseContent = licenseManager.verify();

  34. log.info("证书校验通过,证书有效期:{} - {}",

  35. DateUtil.formatDateTime(licenseContent.getNotBefore()),

  36. DateUtil.formatDateTime(licenseContent.getNotAfter()));

  37. return true;

  38. } catch (Exception e) {

  39. log.error("证书校验失败!", e);

  40. return false;

  41. }

  42. }

  43. /**

  44. * 初始化证书生成参数

  45. */

  46. private LicenseParam initLicenseParam(LicenseVerifyParam param) {

  47. Preferences preferences = Preferences.userNodeForPackage(LicenseVerify.class);

  48. CipherParam cipherParam = new DefaultCipherParam(param.getStorePass());

  49. KeyStoreParam publicStoreParam = new CustomKeyStoreParam(LicenseVerify.class

  50. , param.getPublicKeysStorePath()

  51. , param.getPublicAlias()

  52. , param.getStorePass()

  53. , null);

  54. return new DefaultLicenseParam(param.getSubject()

  55. , preferences

  56. , publicStoreParam

  57. , cipherParam);

  58. }

  59. }

LicenseVerifyParam

 
  1. /**

  2. * License校验类需要的参数

  3. *

  4. */

  5. @Data

  6. @NoArgsConstructor

  7. @AllArgsConstructor

  8. public class LicenseVerifyParam {

  9. /**

  10. * 证书subject

  11. */

  12. private String subject;

  13. /**

  14. * 公钥别称

  15. */

  16. private String publicAlias;

  17. /**

  18. * 访问公钥库的密码

  19. */

  20. private String storePass;

  21. /**

  22. * 证书生成路径

  23. */

  24. private String licensePath;

  25. /**

  26. * 密钥库存储路径

  27. */

  28. private String publicKeysStorePath;

  29. }

LicenseCheckListener

 
  1. /**

  2. * 在项目启动时安装证书

  3. *

  4. */

  5. @Slf4j

  6. @Component

  7. public class LicenseCheckListener implements ApplicationListener<ContextRefreshedEvent> {

  8. /**

  9. * 证书subject

  10. */

  11. @Value("${license.subject}")

  12. private String subject;

  13. /**

  14. * 公钥别称

  15. */

  16. @Value("${license.publicAlias}")

  17. private String publicAlias;

  18. /**

  19. * 访问公钥库的密码

  20. */

  21. @Value("${license.storePass}")

  22. private String storePass;

  23. /**

  24. * 证书生成路径

  25. */

  26. @Value("${license.licensePath}")

  27. private String licensePath;

  28. /**

  29. * 密钥库存储路径

  30. */

  31. @Value("${license.publicKeysStorePath}")

  32. private String publicKeysStorePath;

  33. @Override

  34. public void onApplicationEvent(ContextRefreshedEvent event) {

  35. //root application context 没有parent

  36. ApplicationContext context = event.getApplicationContext().getParent();

  37. if (context == null) {

  38. if (StrUtil.isNotBlank(licensePath)) {

  39. log.info("++++++++ 开始安装证书 ++++++++");

  40. LicenseVerifyParam param = new LicenseVerifyParam();

  41. param.setSubject(subject);

  42. param.setPublicAlias(publicAlias);

  43. param.setStorePass(storePass);

  44. param.setLicensePath(licensePath);

  45. param.setPublicKeysStorePath(publicKeysStorePath);

  46. LicenseVerify licenseVerify = new LicenseVerify();

  47. //安装证书

  48. licenseVerify.install(param);

  49. log.info("++++++++ 证书安装结束 ++++++++");

  50. }

  51. }

  52. }

  53. }

LicenseCheckInterceptor

 
  1. /**

  2. * 拦截器校验证书是否合法

  3. *

  4. */

  5. @Slf4j

  6. @Component

  7. public class LicenseCheckInterceptor extends HandlerInterceptorAdapter {

  8. @Override

  9. public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {

  10. LicenseVerify licenseVerify = new LicenseVerify();

  11. // 校验证书是否有效

  12. boolean verifyResult = licenseVerify.verify();

  13. if (verifyResult) {

  14. return true;

  15. } else {

  16. log.warn("您的证书无效,请核查服务器是否取得授权或重新申请证书!");

  17. throw new BusinessException("您的证书无效,请核查服务器是否取得授权或重新申请证书!");

  18. }

  19. }

  20. }

完。

  • 0
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值