文章目录
一、License介绍
**License也就是版权许可证书,一般用于
收费软件
给付费用户提供的
访问许可证明
**
应用场景
- 应用部署在客户的内网环境
- 这种情况开发者无法控制客户的网络环境,也不能保证应用所在服务器可以访问外网
- 因此通常的做法是使用服务器许可文件,在
应用启动
的时候加载证书
- 然后在登录或者其他关键操作的地方校验证书的
有效性
License授权原理
- **使用开源的证书管理引擎
TrueLicense
**1. 生成密钥对,使用Keytool生成公私钥证书库2. 授权者保留私钥,使用私钥和使用日期生成证书license3. 公钥与生成的证书给使用者(放在验证的代码中使用),验证证书license是否在有效期内
二、授权者生成密钥对
- 需要关注以及修改的参数:storepass(私钥库的密码)、keypass(私钥的密码)
- 其他参数使用默认值即可,validity(私钥的有效期)设置十年就可以,因为以后会通过私钥和证书有效期生成证书license
## 1. 生成私匙库# validity:私钥的有效期(单位:天)# alias:私钥别称# keystore: 私钥库文件名称(生成在当前目录)# storepass:私钥库的密码(获取keystore信息所需的密码) # keypass:私钥的密码# dname 证书个人信息# CN 为你的姓名# OU 为你的组织单位名称# O 为你的组织名称# L 为你所在的城市名称# ST 为你所在的省份名称# C 为你的国家名称 或 区号
keytool -genkeypair-keysize1024-validity3650-alias"privateKey"-keystore"privateKeys.keystore"-storepass"public_password1234"-keypass"private_password1234"-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"public_password1234"-file"certfile.cer"## 3. 再把这个证书文件导入到公匙库# alias:公钥别称# file:证书名称# keystore:公钥文件名称(生成在当前目录)# storepass:私钥库的密码
keytool -import-alias"publicCert"-file"certfile.cer"-keystore"publicCerts.keystore"-storepass"public_password1234"
上述命令执行完成后会在当前目录生成三个文件:
- certfile.cer 认证证书文件,已经没用了,可以删除
- privateKeys.keystore 私钥文件,自己保存,以后用于生成license.lic证书
- publicKeys.keystore 公钥文件,以后会和license.lic证书一起放到使用者项目里
三、授权者生成license.lic证书
pom.xml
<!-- License --><dependency><groupId>de.schlichtherle.truelicense</groupId><artifactId>truelicense-core</artifactId><version>1.33</version></dependency>
1、License生成类
importde.schlichtherle.license.*;importlombok.extern.slf4j.Slf4j;importjavax.security.auth.x500.X500Principal;importjava.io.File;importjava.io.IOException;importjava.time.LocalDateTime;importjava.time.ZoneId;importjava.util.Date;importjava.util.prefs.Preferences;@Slf4jpublicclassLicenseCreator{privatefinalstaticX500PrincipalDEFAULT_HOLDER_AND_ISSUER=newX500Principal("CN=localhost, OU=localhost, O=localhost, L=SH, ST=SH, C=CN");/**
* @description main方法生成license文件
* @author xuchang
* @date 2024/3/4 15:51:14
*/publicstaticvoidmain(String[] args)throwsIOException{LicenseCreatorParam param =newLicenseCreatorParam();// 证书主题
param.setSubject("license");// 密钥别称
param.setPrivateAlias("privateKey");// 私钥密码
param.setKeyPass("private_password1234");// 私钥库的密码
param.setStorePass("public_password1234");// 证书生成路径
param.setLicensePath("/Users/xuchang/Documents/license/license.lic");// 私钥存储路径
param.setPrivateKeysStorePath("/Users/xuchang/Documents/license/privateKeys.keystore");// 证书生成时间-当前时间
param.setIssuedTime(newDate());LocalDateTime localDateTime =LocalDateTime.of(2024,12,31,23,59,59);Date date =Date.from(localDateTime.atZone(ZoneId.systemDefault()).toInstant());// 证书过期时间-2024年12月31日23:59:59
param.setExpiryTime(date);// 用户类型
param.setConsumerType("user");// 用户数量
param.setConsumerAmount(1);// 证书描述
param.setDescription("证书描述信息");// 生成证书LicenseCreator licenseCreator =newLicenseCreator();
licenseCreator.generateLicense(param);}// 生成License证书publicvoidgenerateLicense(LicenseCreatorParam param){try{LicenseManager licenseManager =newLicenseManager(initLicenseParam(param));LicenseContent licenseContent =initLicenseContent(param);
licenseManager.store(licenseContent,newFile(param.getLicensePath()));}catch(Exception e){
log.error("证书生成失败", e);}}// 初始化证书生成参数privatestaticLicenseParaminitLicenseParam(LicenseCreatorParam param){Preferences preferences =Preferences.userNodeForPackage(LicenseCreator.class);// 设置对证书内容加密的秘钥CipherParam cipherParam =newDefaultCipherParam(param.getStorePass());// 自定义KeyStoreParamKeyStoreParam privateStoreParam =newCustomKeyStoreParam(LicenseCreator.class, param.getPrivateKeysStorePath(), param.getPrivateAlias(), param.getStorePass(), param.getKeyPass());// 组织License参数returnnewDefaultLicenseParam(param.getSubject(), preferences
, privateStoreParam
, cipherParam);}// 设置证书生成正文信息privatestaticLicenseContentinitLicenseContent(LicenseCreatorParam param){LicenseContent licenseContent =newLicenseContent();
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());return licenseContent;}}
License生成类需要的参数类
@DatapublicclassLicenseCreatorParam{/**
* 证书subject
*/privateString subject;/**
* 密钥别称
*/privateString privateAlias;/**
* 公钥密码(需要妥善保管,不能让使用者知道)
*/privateString keyPass;/**
* 私钥库的密码
*/privateString storePass;/**
* 证书生成路径
*/privateString licensePath;/**
* 私钥存储路径
*/privateString privateKeysStorePath;/**
* 证书生效时间
*/@JsonFormat(pattern ="yyyy-MM-dd HH:mm:ss", timezone ="GMT+8")privateDate issuedTime;/**
* 证书失效时间
*/@JsonFormat(pattern ="yyyy-MM-dd HH:mm:ss", timezone ="GMT+8")privateDate expiryTime;/**
* 用户类型
*/privateString consumerType;/**
* 用户数量
*/privateInteger consumerAmount;/**
* 描述信息
*/privateString description;}
自定义KeyStoreParam
publicclassCustomKeyStoreParamextendsAbstractKeyStoreParam{privatefinalString storePath;privatefinalString alias;privatefinalString storePwd;privatefinalString keyPwd;publicCustomKeyStoreParam(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;}@OverridepublicStringgetAlias(){return alias;}@OverridepublicStringgetStorePwd(){return storePwd;}@OverridepublicStringgetKeyPwd(){return keyPwd;}@OverridepublicInputStreamgetStream()throwsIOException{returnFiles.newInputStream(Paths.get(storePath));}}
2、main方法生成license.lic注意事项
- 以上都是授权者需要做的,下面说下使用者需要的配置
四、使用者配置
pom.xml
<!-- License --><dependency><groupId>de.schlichtherle.truelicense</groupId><artifactId>truelicense-core</artifactId><version>1.33</version></dependency>
1、License校验类
@Slf4jpublicclassLicenseVerify{// 安装License证书publicsynchronizedLicenseContentinstall(LicenseVerifyParam param){LicenseContent result =null;DateFormat format =newSimpleDateFormat("yyyy-MM-dd HH:mm:ss");// 1. 安装证书try{LicenseManager licenseManager =LicenseManagerHolder.getInstance(initLicenseParam(param));
licenseManager.uninstall();
result = licenseManager.install(newFile(param.getLicensePath()));
log.info(MessageFormat.format("证书安装成功,证书有效期:{0} - {1}",
format.format(result.getNotBefore()), format.format(result.getNotAfter())));}catch(Exception e){
log.error("证书安装失败: {}", e.getMessage());}return result;}// 校验License证书publicbooleanverify(){LicenseManager licenseManager =LicenseManagerHolder.getInstance(null);DateFormat format =newSimpleDateFormat("yyyy-MM-dd HH:mm:ss");// 2. 校验证书try{LicenseContent licenseContent = licenseManager.verify();
log.info(MessageFormat.format("证书校验通过,证书有效期:{0} - {1}",
format.format(licenseContent.getNotBefore()), format.format(licenseContent.getNotAfter())));returntrue;}catch(Exception e){
log.error("证书校验失败: {}", e.getMessage());returnfalse;}}// 初始化证书生成参数privateLicenseParaminitLicenseParam(LicenseVerifyParam param){Preferences preferences =Preferences.userNodeForPackage(LicenseVerify.class);CipherParam cipherParam =newDefaultCipherParam(param.getStorePass());KeyStoreParam publicStoreParam =newCustomKeyStoreParam(LicenseVerify.class, param.getPublicKeysStorePath(), param.getPublicAlias(), param.getStorePass(),null);returnnewDefaultLicenseParam(param.getSubject(), preferences
, publicStoreParam
, cipherParam);}}
License校验类需要的参数
@DatapublicclassLicenseVerifyParam{/**
* 证书subject
*/privateString subject;/**
* 公钥别称
*/privateString publicAlias;/**
* 访问公钥库的密码
*/privateString storePass;/**
* 证书生成路径
*/privateString licensePath;/**
* 密钥库存储路径
*/privateString publicKeysStorePath;}
自定义KeyStoreParam(与授权者一样)
publicclassCustomKeyStoreParamextendsAbstractKeyStoreParam{privatefinalString storePath;privatefinalString alias;privatefinalString storePwd;privatefinalString keyPwd;publicCustomKeyStoreParam(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;}@OverridepublicStringgetAlias(){return alias;}@OverridepublicStringgetStorePwd(){return storePwd;}@OverridepublicStringgetKeyPwd(){return keyPwd;}@OverridepublicInputStreamgetStream()throwsIOException{returnFiles.newInputStream(Paths.get(storePath));}}
LicenseManager的单例
publicclassLicenseManagerHolder{privatestaticvolatileLicenseManagerLICENSE_MANAGER;publicstaticLicenseManagergetInstance(LicenseParam param){if(LICENSE_MANAGER==null){synchronized(LicenseManagerHolder.class){if(LICENSE_MANAGER==null){LICENSE_MANAGER=newLicenseManager(param);}}}returnLICENSE_MANAGER;}}
2、项目启动时安装证书
- application.poperties配置
#License配置# 与创建license.lic设置的值一样license.subject=license
# 与生成密钥对的公钥别称一样license.publicAlias=publicCert
# 私钥库的密码license.storePass=public_password1234
- license.lic证书和publicCerts.keystore放入resources资源目录下
- springboot项目启动后执行操作
@Slf4j@ComponentpublicclassLicenseCheckRunnerimplementsApplicationRunner{/**
* 证书subject
*/@Value("${license.subject}")privateString subject;/**
* 公钥别称
*/@Value("${license.publicAlias}")privateString publicAlias;/**
* 访问公钥库的密码
*/@Value("${license.storePass}")privateString storePass;@Overridepublicvoidrun(ApplicationArguments args)throwsException{
log.info("++++++++ 开始安装证书 ++++++++");LicenseVerifyParam param =newLicenseVerifyParam();
param.setSubject(subject);
param.setPublicAlias(publicAlias);
param.setStorePass(storePass);// 相对路径resources资源目录String resourcePath =getClass().getClassLoader().getResource("").getPath();// 证书地址
param.setLicensePath(resourcePath +"license.lic");// 公钥地址
param.setPublicKeysStorePath(resourcePath +"publicCerts.keystore");// 安装证书LicenseVerify licenseVerify =newLicenseVerify();
licenseVerify.install(param);
log.info("++++++++ 证书安装结束 ++++++++");}}
- 如果想要当前配置作为公共类,对于多个微服务,只想要一个服务resources/license下配置证书和公钥
- 获取公共服务里证书和公钥的输入流,然后拷贝到当前服务下
- 启动安装成功
- 启动安装失败(证书过期)
3、设置拦截器
配置拦截器
@Slf4jpublicclassLicenseCheckInterceptorimplementsHandlerInterceptor{@OverridepublicbooleanpreHandle(HttpServletRequest request,HttpServletResponse response,Object handler)throwsException{LicenseVerify licenseVerify =newLicenseVerify();// 校验证书是否有效boolean verifyResult = licenseVerify.verify();if(verifyResult){returntrue;}else{
response.setCharacterEncoding("utf-8");Map<String,String> result =newHashMap<>(1);
result.put("result","您的证书无效,请核查服务器是否取得授权或重新申请证书!");
response.getWriter().write(JSON.toJSONString(result));returnfalse;}}}
注册拦截器
@ConfigurationpublicclassWebMvcConfigimplementsWebMvcConfigurer{@OverridepublicvoidaddInterceptors(InterceptorRegistry registry){
registry.addInterceptor(newLicenseCheckInterceptor());}}
- 证书有效期内请求接口
- 证书过期请求接口
版权归原作者 冬天vs不冷 所有, 如有侵权,请联系我们删除。