小工具知识网

当前位置: 首页 > 行业知识 > SpringBoot实现License认证(只校验有效期)

行业知识

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

2026-11-02 08:13:57 来源:互联网转载

文章目录

  • 一、License介绍
  • 二、授权者生成密钥对
  • 三、授权者生成license.lic证书
    • 1、License生成类
    • 2、main方法生成license.lic注意事项
    • 四、使用者配置
      • 1、License校验类
      • 2、项目启动时安装证书
      • 3、设置拦截器

        一、License介绍

        License也就是版权许可证书,一般用于收费软件给付费用户提供的访问许可证明

        应用场景

        • 应用部署在客户的内网环境
        • 这种情况开发者无法控制客户的网络环境,也不能保证应用所在服务器可以访问外网
        • 因此通常的做法是使用服务器许可文件,在应用启动的时候加载证书
        • 然后在登录或者其他关键操作的地方校验证书的有效性

          License授权原理

          • 使用开源的证书管理引擎TrueLicense
            1. 生成密钥对,使用Keytool生成公私钥证书库
            2. 授权者保留私钥,使用私钥和使用日期生成证书license
            3. 公钥与生成的证书给使用者(放在验证的代码中使用),验证证书license是否在有效期内

            二、授权者生成密钥对

            • 需要关注以及修改的参数:storepass(私钥库的密码)、keypass(私钥的密码)
            • 其他参数使用默认值即可,validity(私钥的有效期)设置十年就可以,因为以后会通过私钥和证书有效期生成证书license
              ## 1. 生成私匙库# validity:私钥的有效期(单位:天)# alias:私钥别称# keystore: 私钥库文件名称(生成在当前目录)# storepass:私钥库的密码(获取keystore信息所需的密码) # keypass:私钥的密码# dname 证书个人信息# CN 为你的姓名#OU 为你的组织单位名称#O 为你的组织名称#L 为你所在的城市名称#ST 为你所在的省份名称#C 为你的国家名称 或 区号keytool -genkeypair -keysize 1024 -validity 3650 -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

                  de.schlichtherle.truelicense    truelicense-core    1.33

              1、License生成类

              import de.schlichtherle.license.*;import lombok.extern.slf4j.Slf4j;import javax.security.auth.x500.X500Principal;import java.io.File;import java.io.IOException;import java.time.LocalDateTime;import java.time.ZoneId;import java.util.Date;import java.util.prefs.Preferences;@Slf4jpublic class LicenseCreator {    private final static X500Principal DEFAULT_HOLDER_AND_ISSUER =     new X500Principal("CN=localhost, OU=localhost, O=localhost, L=SH, ST=SH, C=CN");    /**     * @description main方法生成license文件     * @author xuchang     * @date 2024/3/4 15:51:14     */    public static void main(String[] args) throws IOException {        LicenseCreatorParam param = new LicenseCreatorParam();        // 证书主题        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(new Date());        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 = new LicenseCreator();        licenseCreator.generateLicense(param);    }    // 生成License证书    public void generateLicense(LicenseCreatorParam param) {        try {            LicenseManager licenseManager = new LicenseManager(initLicenseParam(param));            LicenseContent licenseContent = initLicenseContent(param);            licenseManager.store(licenseContent, new File(param.getLicensePath()));        } catch (Exception e) {            log.error("证书生成失败", e);        }    }    // 初始化证书生成参数    private static LicenseParam initLicenseParam(LicenseCreatorParam param) {        Preferences preferences = Preferences.userNodeForPackage(LicenseCreator.class);        // 设置对证书内容加密的秘钥        CipherParam cipherParam = new DefaultCipherParam(param.getStorePass());        // 自定义KeyStoreParam        KeyStoreParam privateStoreParam = new CustomKeyStoreParam(LicenseCreator.class                , param.getPrivateKeysStorePath()                , param.getPrivateAlias()                , param.getStorePass()                , param.getKeyPass());        // 组织License参数        return new DefaultLicenseParam(param.getSubject()                , preferences                , privateStoreParam                , cipherParam);    }    // 设置证书生成正文信息    private static LicenseContent initLicenseContent(LicenseCreatorParam param) {        LicenseContent licenseContent = new LicenseContent();        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生成类需要的参数类

              @Datapublic class LicenseCreatorParam {    /**     * 证书subject     */    private String subject;    /**     * 密钥别称     */    private String privateAlias;    /**     * 公钥密码(需要妥善保管,不能让使用者知道)     */    private String keyPass;    /**     * 私钥库的密码     */    private String storePass;    /**     * 证书生成路径     */    private String licensePath;    /**     * 私钥存储路径     */    private String privateKeysStorePath;    /**     * 证书生效时间     */    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")    private Date issuedTime;    /**     * 证书失效时间     */    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")    private Date expiryTime;    /**     * 用户类型     */    private String consumerType;    /**     * 用户数量     */    private Integer consumerAmount;    /**     * 描述信息     */    private String description;}

              自定义KeyStoreParam

              public class CustomKeyStoreParam extends AbstractKeyStoreParam {    private final String storePath;    private final String alias;    private final String storePwd;    private final String keyPwd;    public CustomKeyStoreParam(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;    }    @Override    public String getAlias() {        return alias;    }    @Override    public String getStorePwd() {        return storePwd;    }    @Override    public String getKeyPwd() {        return keyPwd;    }    @Override    public InputStream getStream() throws IOException {        return Files.newInputStream(Paths.get(storePath));    }}

              2、main方法生成license.lic注意事项

              • 以上都是授权者需要做的,下面说下使用者需要的配置

                四、使用者配置

                pom.xml

                    de.schlichtherle.truelicense    truelicense-core    1.33

                1、License校验类

                @Slf4jpublic class LicenseVerify {    // 安装License证书    public synchronized LicenseContent install(LicenseVerifyParam param) {        LicenseContent result = null;        DateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");        // 1. 安装证书        try {            LicenseManager licenseManager = LicenseManagerHolder.getInstance(initLicenseParam(param));            licenseManager.uninstall();            result = licenseManager.install(new File(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证书    public boolean verify() {        LicenseManager licenseManager = LicenseManagerHolder.getInstance(null);        DateFormat format = new SimpleDateFormat("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())));            return true;        } catch (Exception e) {            log.error("证书校验失败: {}", e.getMessage());            return false;        }    }    // 初始化证书生成参数    private LicenseParam initLicenseParam(LicenseVerifyParam param) {        Preferences preferences = Preferences.userNodeForPackage(LicenseVerify.class);        CipherParam cipherParam = new DefaultCipherParam(param.getStorePass());        KeyStoreParam publicStoreParam = new CustomKeyStoreParam(LicenseVerify.class                , param.getPublicKeysStorePath()                , param.getPublicAlias()                , param.getStorePass()                , null);        return new DefaultLicenseParam(param.getSubject()                , preferences                , publicStoreParam                , cipherParam);    }}

                License校验类需要的参数

                @Datapublic class LicenseVerifyParam {    /**     * 证书subject     */    private String subject;    /**     * 公钥别称     */    private String publicAlias;    /**     * 访问公钥库的密码     */    private String storePass;    /**     * 证书生成路径     */    private String licensePath;    /**     * 密钥库存储路径     */    private String publicKeysStorePath;}

                自定义KeyStoreParam(与授权者一样)

                public class CustomKeyStoreParam extends AbstractKeyStoreParam {    private final String storePath;    private final String alias;    private final String storePwd;    private final String keyPwd;    public CustomKeyStoreParam(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;    }    @Override    public String getAlias() {        return alias;    }    @Override    public String getStorePwd() {        return storePwd;    }    @Override    public String getKeyPwd() {        return keyPwd;    }    @Override    public InputStream getStream() throws IOException {        return Files.newInputStream(Paths.get(storePath));    }}

                LicenseManager的单例

                public class LicenseManagerHolder {    private static volatile LicenseManager LICENSE_MANAGER;    public static LicenseManager getInstance(LicenseParam param){        if(LICENSE_MANAGER == null){            synchronized (LicenseManagerHolder.class){                if(LICENSE_MANAGER == null){                    LICENSE_MANAGER = new LicenseManager(param);                }            }        }        return LICENSE_MANAGER;    }}

                2、项目启动时安装证书

                • application.poperties配置
                  #License配置# 与创建license.lic设置的值一样license.subject=license# 与生成密钥对的公钥别称一样license.publicAlias=publicCert# 私钥库的密码license.storePass=public_password1234
                  • license.lic证书和publicCerts.keystore放入resources资源目录下
                    • springboot项目启动后执行操作
                      @Slf4j@Componentpublic class LicenseCheckRunner implements ApplicationRunner {    /**     * 证书subject     */    @Value("${license.subject}")    private String subject;    /**     * 公钥别称     */    @Value("${license.publicAlias}")    private String publicAlias;    /**     * 访问公钥库的密码     */    @Value("${license.storePass}")    private String storePass;    @Override    public void run(ApplicationArguments args) throws Exception {        log.info("++++++++ 开始安装证书 ++++++++");        LicenseVerifyParam param = new LicenseVerifyParam();        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 = new LicenseVerify();        licenseVerify.install(param);        log.info("++++++++ 证书安装结束 ++++++++");    }}
                      • 如果想要当前配置作为公共类,对于多个微服务,只想要一个服务resources/license下配置证书和公钥
                      • 获取公共服务里证书和公钥的输入流,然后拷贝到当前服务下
                        • 启动安装成功
                          • 启动安装失败(证书过期)

                            3、设置拦截器

                            配置拦截器

                            @Slf4jpublic class LicenseCheckInterceptor implements HandlerInterceptor {    @Override    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {        LicenseVerify licenseVerify = new LicenseVerify();        // 校验证书是否有效        boolean verifyResult = licenseVerify.verify();        if (verifyResult) {            return true;        } else {            response.setCharacterEncoding("utf-8");            Map result = new HashMap(1);            result.put("result", "您的证书无效,请核查服务器是否取得授权或重新申请证书!");            response.getWriter().write(JSON.toJSONString(result));            return false;        }    }}

                            注册拦截器

                            @Configurationpublic class WebMvcConfig implements WebMvcConfigurer {    @Override    public void addInterceptors(InterceptorRegistry registry) {        registry.addInterceptor(new LicenseCheckInterceptor());    }}
                            • 证书有效期内请求接口
                              • 证书过期请求接口

上一篇:怎么自己建服务器地址?

下一篇:CSS——PostCSS简介