Spring Boot實現許可證生成與驗證
1. 申请许可证的场景。
当我们向客户出售商业软件时,我们经常需要对发布的软件实施一系列的控制措施,例如验证用户的身份、软件是否已过期失效,以及保存版权信息和开发者信息。考虑到许多应用场景可能处于离线环境中,无法依赖网络进行实时验证,我们还需要考虑如何在没有网络支持的情况下防止破解。总之,许可证采用HTTPS网站的证书和签名技术来证明当前用户是申请许可证的合法持有人,并防止恶意破解、伪造或篡改许可证,从而实现免费使用的目的。
为什么要使用许可证授权?许可证的作用是什么?付费软件的许可证目的是为了防止用户免费使用软件。此外,许可证还应该具备以下功能:
- 授权使用:明确用户需要满足的使用条件,如单一用户、多用户、企业内部使用、全球使用等,并通常限制可安装和激活的设备数量。
- 使用期限:规定软件的使用期限,可能是永久许可或订阅模式。过期后,用户需要续订订阅才能继续使用软件。
- 限制功能:根据许可级别,软件可以提供不同级别的功能,如标准版、高级版、企业版等。许可可以解锁相应版本的功能。
- 版权保护:重申软件的知识产权所有权,并禁止未经授权的复制、分发、反编译、篡改或反向工程等版权侵权行为。
- 法律保护:作为法律合同,许可建立了软件提供商与用户之间的法律关系,并明确了双方的权利和责任。如果违反协议,软件提供商有权采取法律行动追究法律责任。
- 技术支持和升级服务:某些许可会规定用户是否享有免费技术支持、软件更新和维护服务,以及这些服务的有效时间。
- 合规要求:对于特定行业或特定地区,许可需符合特定的法规、标准或认证要求。
总而言之,我们可以将许可证的功能总结为:
- 控制用户对软件的访问权限
- 认可软件所有者的版权
- 规定软件的使用方法
例如,如果私钥库的密码是“priwd123456”,公钥库的密码是“pubwd123456”,生成步骤如下所示:
keytool -genkeypair -keysize 1024 -validity 3650 -alias "privateKey" -keystore "privateKeys.keystore" -storepass "pubwd123456" -keypass "priwd123456" -dname "CN=localhost, OU=localhost, O=localhost, L=SH, ST=SH, C=CN" # 生成密钥对,密钥长度为1024位,有效期为3650天,别名为“privateKey”,密钥库为“privateKeys.keystore”,库密码为“pubwd123456”,密钥密码为“priwd123456”,主体名信息为“CN=localhost, OU=localhost, O=localhost, L=SH, ST=SH, C=CN”。
keytool -exportcert -alias "privateKey" -keystore "privateKeys.keystore" -storepass "pubwd123456" -file "certfile.cer" # 导出证书,别名为“privateKey”,从密钥库“privateKeys.keystore”中导出,库密码为“pubwd123456”,输出文件为“certfile.cer”。
keytool -import -alias "publicCert" -file "certfile.cer" -keystore "publicCerts.keystore" -storepass "pubwd123456" # 导入证书,别名为“publicCert”,从文件“certfile.cer”中导入,到密钥库“publicCerts.keystore”,库密码为“pubwd123456”。
3. 代码工程
这个实验的目的
pom.xml (Maven项目中的配置文件)实现许可证的生成及验证过程
<?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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>springboot-demo</artifactId>
<groupId>com.et</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>SoftwareLicense</artifactId>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-autoconfigure</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>de.schlichtherle.truelicense</groupId>
<artifactId>truelicense-core</artifactId>
<version>1.33</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
</dependencies>
</project>
生成许可
先获取服务器信息,再获取生成License.lic所需的信息。
package com.et.license.controller;
import com.et.license.license.LicenseCheckModel;
import com.et.license.license.LicenseCreatorParam;
import com.et.license.service.LicenseCreatorService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.util.Map;
/**
* 许可创建控制器类,用于处理与许可证相关的请求。
*/
@RestController
@RequestMapping("/license")
public class LicenseCreatorController {
/**
* 自动注入的许可证创建服务。
*/
@Autowired
private LicenseCreatorService licenseCreatorService;
/**
* 根据操作系统名称获取服务器信息。
* @param osName 操作系统名称
* @return 许可证检查模型对象
*/
@GetMapping("/getServerInfos")
public LicenseCheckModel getServerInfos(@RequestParam String osName) {
return licenseCreatorService.getServerInfos(osName);
}
/**
* 生成许可证。
* @param param 许可证创建参数
* @return 包含生成的许可证信息的Map对象
*/
@PostMapping("/generateLicense")
public Map<String, Object> generateLicense(@RequestBody LicenseCreatorParam param) {
return licenseCreatorService.generateLicense(param);
}
}
package com.et.license.service;
import com.et.license.license.LicenseCheckModel;
import com.et.license.license.LicenseCreatorParam;
import java.util.Map;
public interface LicenseCreatorService {
LicenseCheckModel getServerInfos(String osName);
Map<String, Object> generateLicense(LicenseCreatorParam param);
}
包 源码 com.et.license.service.impl;
导入 包 com.et.license.license.*;
导入 包 com.et.license.service.LicenseCreatorService;
导入 包 org.springframework.beans.factory.annotation.Autowired;
导入 包 org.springframework.stereotype.Service;
导入 包 org.springframework.util.StringUtils;
导入 包 java.util.HashMap;
导入 包 java.util.Map;
@Service
类 LicenseCreatorServiceImpl 实现 LicenseCreatorService {
@Autowired
私有 LicenseConfig licenseConfig;
@Override
公有 LicenseCheckModel getServerInfos(字符串 osName) {
如果 (StringUtils.isEmpty(osName)) {
osName = System.getProperty("os.name");
}
osName = osName.toLowerCase();
抽象ServerInfos abstractServerInfos = null;
如果 (osName.startsWith("windows")) {
abstractServerInfos = 新的 WindowsServerInfos();
} 否则如果 (osName.startsWith("linux")) {
abstractServerInfos = 新的 LinuxServerInfos();
} 否则 {
abstractServerInfos = 新的 LinuxServerInfos();
}
return abstractServerInfos.getServerInfos();
}
@Override
公有 Map<字符串, Object> generateLicense(LicenseCreatorParam param) {
Map<字符串, Object> resultMap = 新的 HashMap<>(2);
如果 (StringUtils.isEmpty(param.getLicensePath())) {
param.setLicensePath(licenseConfig.getLicensePath());
}
LicenseCreator licenseCreator = 新的 LicenseCreator(param);
boolean result = licenseCreator.generateLicense();
如果 (result) {
resultMap.put("result", "ok");
resultMap.put("msg", param);
} 否则 {
resultMap.put("result", "error");
resultMap.put("msg", "生成失败!");
}
return resultMap;
}
}
安装及卸载许可
package com.et.license.license;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Data
@Configuration
@ConfigurationProperties("license")
@Slf4j
public class LicenseConfig {
private String subject;
private String publicAlias;
private String storePass;
private String licensePath;
private String publicKeysStorePath;
@Bean(initMethod = "installLicense", destroyMethod = "unInstallLicense")
public LicenseVerify licenseVerify() {
return new LicenseVerify(subject, publicAlias, storePass, licensePath, publicKeysStorePath);
}
}
校准证书
package com.et.license;
import com.et.license.license.LicenseVerify;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
@SpringBootTest
class TrueLicenseApplicationTests {
@Autowired
private LicenseVerify licenseVerify;
@Test
void contextLoads() {
System.out.println("license valid:" + licenseVerify.verify());
}
}
以上仅列出了一些关键代码片段。如需查看更多代码,访问以下代码仓库即可。
代码库 4. 测试运行 Spring Boot 应用
查看服务器信息请求的内容
{
"subject":"许可证演示",
"privateAlias":"私钥别名",
"keyPass":"密钥密码",
"storePass":"存储密码",
"licensePath":"/Users/liuhaihua/IdeaProjects/springboot-demo/SoftwareLicense/src/main/resources/license/license.lic",
"privateKeysStorePath":"/Users/liuhaihua/IdeaProjects/springboot-demo/SoftwareLicense/src/main/resources/license/privateKeys.keystore",
"issuedTime":"2020-10-10 00:00:01",
"expiryTime":"2025-10-09 23:59:59",
"consumerType":"消费类型",
"consumerAmount":1,
"description":"许可证演示",
"licenseCheckModel":{
"ipAddress": [
"192.168.0.100"
],
"macAddress": [
"F0-18-98-50-71-5F"
],
"cpuSerial": null,
"mainBoardSerial": null
}
}
就是结果
2024-09-02 21:01:54.991 WARN 39586 --- [主线程] o.s.boot.StartupInfoLogger : InetAddress.getLocalHost().getHostName() 响应耗时 5003 毫秒。请验证您的网络配置(macOS 机器可能需要在 /etc/hosts 中添加条目)。
2024-09-02 21:01:59.999 信息 39586 --- [主线程] com.et.license.DemoApplication : 在 bogon 上启动 DemoApplication,进程 ID 39586 (/Users/liuhaihua/IdeaProjects/springboot-demo/SoftwareLicense/target/classes,由 liuhaihua 在 /Users/liuhaihua/IdeaProjects/springboot-demo 启动)
2024-09-02 21:02:00.001 信息 39586 --- [主线程] com.et.license.DemoApplication : 没有激活的配置文件,回退到默认配置文件:default
2024-09-02 21:02:00.767 信息 39586 --- [主线程] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat 使用端口 8080 (http) 初始化
2024-09-02 21:02:00.774 信息 39586 --- [主线程] o.apache.catalina.core.StandardService : 启动服务[Tomcat]
2024-09-02 21:02:00.774 信息 39586 --- [主线程] org.apache.catalina.core.StandardEngine : 启动 Servlet 引擎[A=Apache Tomcat/9.0.31]
2024-09-02 21:02:00.831 信息 39586 --- [主线程] o.a.c.c.C.[Tomcat].[localhost].[/] : 初始化 Spring 嵌入式的 WebApplicationContext
2024-09-02 21:02:00.832 信息 39586 --- [主线程] o.s.web.context.ContextLoader : 根 WebApplicationContext 初始化完成,耗时 793 毫秒
2024-09-02 21:02:01.128 信息 39586 --- [主线程] com.et.license.license.LicenseVerify : ------------------------------- 安装 -------------------------------
2024-09-02 21:02:01.128 信息 39586 --- [主线程] com.et.license.license.LicenseVerify : 有效日期:2020-10-10 至 2025-10-09
2024-09-02 21:02:01.210 信息 39586 --- [主线程] o.s.s.concurrent.ThreadPoolTaskExecutor : 初始化 ExecutorService 'applicationTaskExecutor'
2024-09-02 21:02:01.344 信息 39586 --- [主线程] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat 在端口 8080 (http) 上启动,上下文路径为空字符串
2024-09-02 21:02:01.347 信息 39586 --- [主线程] com.et.license.DemoApplication : DemoApplication 启动耗时 16.649 秒,JVM 运行时间为 22.127 秒
2024-09-02 21:09:41.535 信息 39586 --- [nio-8080-exec-1] o.a.c.c.C.[Tomcat].[localhost].[/] : 初始化 Spring DispatcherServlet 'dispatcherServlet'
2024-09-02 21:09:41.538 信息 39586 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet : 初始化 Servlet 'dispatcherServlet'
2024-09-02 21:09:41.588 信息 39586 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet : 初始化完成,耗时 50 毫秒
校准证明
执行测试案例
2024-09-02 21:17:17.212 INFO 39937 --- [main] com.et.license.license.LicenseVerify : 证书的有效期限:2020-10-10 00:00:01 - 2025-10-09 23:59:59
证书有效: 是,
5. 参考资料
- https://github.com/JCXTB/TrueLicense (TrueLicense项目页面)
- http://www.liuhaihua.cn/archives/711280.html (刘海华的文章)
點擊查看更多內容
為 TA 點贊
評論
評論
共同學習,寫下你的評論
評論加載中...
作者其他優質文章
正在加載中
感謝您的支持,我會繼續努力的~
掃碼打賞,你說多少就多少
贊賞金額會直接到老師賬戶
支付方式
打開微信掃一掃,即可進行掃碼打賞哦