SpringBoot实现License生成和校验的过程详解
1.License应用场景
在我们向客户销售商业软件的时候,常常需要对所发布的软件实行一系列管控措施,诸如验证使用者身份、软件是否到期,以及保存版权信息和开发商详情等。考虑到诸多应用场景可能处于离线环境,无法依赖网络进行实时认证,所以还需要考虑单机认证时的防破解问题。总之,License许可证利用HTTPS网站的证书和签名技术,一方面证明当前使用者是申请License的本人,另一方面要防止恶意破解,并伪造篡改License达到白嫖的目的。
为什么使用License授权?
License的作用是什么呢?收费软件的License其目的肯定是防止用户白嫖啦,所以License还应该具有以下一些功能:
- 授权使用: 明确用户需要满足的使用条件,如单用户、多用户、企业内部使用、全球使用等,并且通常会限定可安装和激活的设备数量。
- 期限控制: 规定软件的使用期限,可能是永久授权,也可能是订阅式授权,到期后用户需要续费才能继续使用。
- 限制功能: 根据不同等级的License,软件可以提供不同等级的功能,例如基础版、专业版、企业版等,License可以解锁相应版本的功能。
- 版权保护: 重申软件的知识产权归属,禁止未经授权的复制、分发、反编译、篡改或逆向工程等侵犯版权的行为。
- 法律保障: License作为法律合同,确立了软件提供商和用户之间的法律关系,明确了双方的权利和责任,如果发生违反协议的情况,软件提供商有权采取法律手段追究责任。
- 技术支持和升级服务:部分License中会规定用户是否有权享有免费的技术支持、软件更新和维护服务,以及这些服务的有效期限。
- 合规性要求: 对于特殊行业或特定地区,License可能还涉及到满足特定法规、标准或认证的要求。
归纳起来,我们可以总结出License的作用是:
- 控制软件使用者的使用权限
- 申明软件所有者的版权
- 规定软件的使用规范
2.证书准备
例如:私钥库密码为 priwd123456,公钥库密码为 pubwd123456,生成步骤如下:
# 1. 生成私钥库 # validity:私钥的有效期(天) # alias:私钥别称 # keystore:私钥库文件名称(生成在当前目录) # storepass:私钥库密码(获取 keystore 信息所需的密码,密钥库口令) # keypass:别名条目的密码(密钥口令) 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" # 2. 把私钥库内的公钥导出到一个文件当中 # alias:私钥别称 # keystore:私钥库的名称(在当前目录查找) # storepass:私钥库的密码 # file:证书名称 keytool -exportcert -alias "privateKey" -keystore "privateKeys.keystore" -storepass "pubwd123456" -file "certfile.cer" # 3.再把这个证书文件导入到公钥库,certfile.cer 没用了可以删掉了 # alias:公钥名称 # file:证书名称 # keystore:公钥文件名称 # storepass:公钥库密码 keytool -import -alias "publicCert" -file "certfile.cer" -keystore "publicCerts.keystore" -storepass "pubwd123456"
3.代码工程
实验目标
实现license生成和校验
pom.xml
<?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
先获取服务器信息,然后拿到相关信息在生成License.lic
/** * Project Name: true-license * File Name: LicenseCreatorController * Package Name: com.example.demo.controller * Date: 2020/10/10 13:36 * Author: 方瑞冬 */ 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; /** * @author 方瑞冬 */ @RestController @RequestMapping("/license") public class LicenseCreatorController { @Autowired private LicenseCreatorService licenseCreatorService; /** * <p>项目名称: true-license-demo </p> * <p>文件名称: LicenseCreatorController.java </p> * <p>方法描述: 获取服务器硬件信息 </p> * <p>创建时间: 2020/10/10 13:39 </p> * * @param osName 系统名称 * @return com.example.demo.license.LicenseCheckModel * @author 方瑞冬 * @version 1.0 */ @GetMapping("/getServerInfos") public LicenseCheckModel getServerInfos(@RequestParam String osName) { return licenseCreatorService.getServerInfos(osName); } /** * <p>项目名称: true-license-demo </p> * <p>文件名称: LicenseCreatorController.java </p> * <p>方法描述: 生成证书 </p> * <p>创建时间: 2020/10/10 13:42 </p> * * @param param 证书创建参数 * @return java.util.Map<java.lang.String, java.lang.Object> * @author 方瑞冬 * @version 1.0 */ @PostMapping("/generateLicense") public Map<String, Object> generateLicense(@RequestBody LicenseCreatorParam param) { return licenseCreatorService.generateLicense(param); } } /** * Project Name: true-license * File Name: LicenseCreatorService * Package Name: com.example.demo.service * Date: 2020/10/10 13:44 * Author: 方瑞冬 */ package com.et.license.service; import com.et.license.license.LicenseCheckModel; import com.et.license.license.LicenseCreatorParam; import java.util.Map; /** * @author 方瑞冬 */ public interface LicenseCreatorService { /** * <p>项目名称: true-license-demo </p> * <p>文件名称: LicenseCreatorService.java </p> * <p>方法描述: 获取服务器硬件信息 </p> * <p>创建时间: 2020/10/10 13:45 </p> * * @param osName 系统名称 * @return com.example.demo.license.LicenseCheckModel * @author 方瑞冬 * @version 1.0 */ LicenseCheckModel getServerInfos(String osName); /** * <p>项目名称: true-license-demo </p> * <p>文件名称: LicenseCreatorService.java </p> * <p>方法描述: 生成证书 </p> * <p>创建时间: 2020/10/10 13:45 </p> * * @param param 证书创建参数 * @return java.util.Map<java.lang.String, java.lang.Object> * @author 方瑞冬 * @version 1.0 */ Map<String, Object> generateLicense(LicenseCreatorParam param); } /** * Project Name: true-license * File Name: LicenseCreatorServiceImpl * Package Name: com.example.demo.service.impl * Date: 2020/10/10 13:44 * Author: 方瑞冬 */ package com.et.license.service.impl; import com.et.license.license.*; import com.et.license.service.LicenseCreatorService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.util.StringUtils; import java.util.HashMap; import java.util.Map; /** * @author 方瑞冬 */ @Service public class LicenseCreatorServiceImpl implements LicenseCreatorService { @Autowired private LicenseConfig licenseConfig; /** * <p>项目名称: true-license-demo </p> * <p>文件名称: LicenseCreatorServiceImpl.java </p> * <p>方法描述: 获取服务器硬件信息 </p> * <p>创建时间: 2020/10/10 13:46 </p> * * @param osName 系统名称 * @return com.example.demo.license.LicenseCheckModel * @author 方瑞冬 * @version 1.0 */ @Override public LicenseCheckModel getServerInfos(String osName) { //操作系统类型 if (StringUtils.isEmpty(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(); } /** * <p>项目名称: true-license-demo </p> * <p>文件名称: LicenseCreatorServiceImpl.java </p> * <p>方法描述: 生成证书 </p> * <p>创建时间: 2020/10/10 13:46 </p> * * @param param 证书创建参数 * @return java.util.Map<java.lang.String, java.lang.Object> * @author 方瑞冬 * @version 1.0 */ @Override public Map<String, Object> generateLicense(LicenseCreatorParam param) { Map<String, Object> resultMap = new HashMap<>(2); if (StringUtils.isEmpty(param.getLicensePath())) { param.setLicensePath(licenseConfig.getLicensePath()); } 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; } }
安装和卸载license
/** * Project Name: true-license * File Name: LicenseConfig * Package Name: com.example.demo.license * Date: 2020/10/10 14:03 * Author: 方瑞冬 */ 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; /** * @author 方瑞冬 * 证书配置 */ @Data @Configuration @ConfigurationProperties("license") @Slf4j public class LicenseConfig { /** * 证书 subject */ private String subject; /** * 公钥别称 */ private String publicAlias; /** * 访问公钥库的密码 */ private String storePass; /** * 证书生成路径 */ private String licensePath; /** * 密钥库存储路径 */ private String publicKeysStorePath; /** * <p>项目名称: true-license-demo </p> * <p>文件名称: LicenseConfig.java </p> * <p>方法描述: 把 LicenseVerify 类添加到 Spring 容器。在 LicenseVerify 从 Spring 容器添加或移除的时候调用证书安装或卸载 </p> * <p>创建时间: 2020/10/10 16:07 </p> * * @return com.example.demo.licensegenerate.LicenseVerify * @author 方瑞冬 * @version 1.0 */ @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("licese是否有效:" + licenseVerify.verify()); } }
以上只是一些关键代码,所有代码请参见下面代码仓库
代码仓库
4.测试
启动Spring Boot应用
获取服务器信息
生成License.lic
请求报文
{ "subject":"license_demo", "privateAlias":"privateKey", "keyPass":"priwd123456", "storePass":"pubwd123456", "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":"User", "consumerAmount":1, "description":"license demo", "licenseCheckModel":{ "ipAddress": [ "192.168.0.100" ], "macAddress": [ "F0-18-98-50-71-5F" ], "cpuSerial": null, "mainBoardSerial": null } }
结果
安装证书
024-09-02 21:01:54.991 WARN 39586 --- [ main] o.s.boot.StartupInfoLogger : InetAddress.getLocalHost().getHostName() took 5003 milliseconds to respond. Please verify your network configuration (macOS machines may need to add entries to /etc/hosts). 2024-09-02 21:01:59.999 INFO 39586 --- [ main] com.et.license.DemoApplication : Starting DemoApplication on bogon with PID 39586 (/Users/liuhaihua/IdeaProjects/springboot-demo/SoftwareLicense/target/classes started by liuhaihua in /Users/liuhaihua/IdeaProjects/springboot-demo) 2024-09-02 21:02:00.001 INFO 39586 --- [ main] com.et.license.DemoApplication : No active profile set, falling back to default profiles: default 2024-09-02 21:02:00.767 INFO 39586 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat initialized with port(s): 8080 (http) 2024-09-02 21:02:00.774 INFO 39586 --- [ main] o.apache.catalina.core.StandardService : Starting service [Tomcat] 2024-09-02 21:02:00.774 INFO 39586 --- [ main] org.apache.catalina.core.StandardEngine : Starting Servlet engine: [Apache Tomcat/9.0.31] 2024-09-02 21:02:00.831 INFO 39586 --- [ main] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring embedded WebApplicationContext 2024-09-02 21:02:00.832 INFO 39586 --- [ main] o.s.web.context.ContextLoader : Root WebApplicationContext: initialization completed in 793 ms 2024-09-02 21:02:01.128 INFO 39586 --- [ main] com.et.license.license.LicenseVerify : ------------------------------- 证书安装成功 ------------------------------- 2024-09-02 21:02:01.128 INFO 39586 --- [ main] com.et.license.license.LicenseVerify : 证书校验通过,证书有效期:2020-10-10 00:00:01 - 2025-10-09 23:59:59 2024-09-02 21:02:01.210 INFO 39586 --- [ main] o.s.s.concurrent.ThreadPoolTaskExecutor : Initializing ExecutorService 'applicationTaskExecutor' 2024-09-02 21:02:01.344 INFO 39586 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat started on port(s): 8080 (http) with context path '' 2024-09-02 21:02:01.347 INFO 39586 --- [ main] com.et.license.DemoApplication : Started DemoApplication in 16.649 seconds (JVM running for 22.127) 2024-09-02 21:09:41.535 INFO 39586 --- [nio-8080-exec-1] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring DispatcherServlet 'dispatcherServlet' 2024-09-02 21:09:41.538 INFO 39586 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet : Initializing Servlet 'dispatcherServlet' 2024-09-02 21:09:41.588 INFO 39586 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet : Completed initialization in 50 ms
校验证书
运行测试用例
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 licese是否有效:true
以上就是SpringBoot实现License生成和校验的过程详解的详细内容,更多关于SpringBoot License生成和校验的资料请关注脚本之家其它相关文章!
相关文章
501 Command "HELO" requires an argument问题的解决方法
换一个windows服务器,发现就没这样的问题,仅在一台Linux服务器上可以重现,直观感觉就是这台Linux服务器某些配置有问题2013-08-08Java常用类库Apache Commons工具类说明及使用实例详解
这篇文章主要介绍了Java常用类库Apache Commons工具类说明及使用实例详解,需要的朋友可以参考下2020-02-02Mybatis-Plus中getOne方法获取最新一条数据的示例代码
这篇文章主要介绍了Mybatis-Plus中getOne方法获取最新一条数据,本文通过示例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下2023-05-05spring boot 使用Aop通知打印控制器请求报文和返回报文问题
这篇文章主要介绍了spring-boot 使用Aop通知打印控制器请求报文和返回报文,非常不错,具有参考借鉴价值,需要的朋友可以参考下2018-04-04
最新评论