0


SpringBoot实现License认证(只校验有效期)

文章目录

一、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"

上述命令执行完成后会在当前目录生成三个文件:

  1. certfile.cer 认证证书文件,已经没用了,可以删除
  2. privateKeys.keystore 私钥文件,自己保存,以后用于生成license.lic证书
  3. 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());}}
  • 证书有效期内请求接口

在这里插入图片描述

  • 证书过期请求接口

在这里插入图片描述

标签: spring boot https ssl

本文转载自: https://blog.csdn.net/qq_35512802/article/details/136497252
版权归原作者 冬天vs不冷 所有, 如有侵权,请联系我们删除。

“SpringBoot实现License认证(只校验有效期)”的评论:

还没有评论