SpringBoot整合TrueLicense生成和验证License证书

一 License介绍

本博客用到的项目资料:

链接: https://pan.baidu.com/s/1y0vxCd3gB0RTNNKtUNg8Tw 提取码: gsni

License,也就是版权许可证书,一般用于收费软件给付费用户提供的访问许可证明。根据应用部署位置的不同,一般可以分为以下几种情况讨论:

  • 应用部署在开发者自己的云服务器上。这种情况下用户通过账号登录的形式远程访问,因此只需要在账号登录的时候校验目标账号的有效期、访问权限等信息即可。
  • 应用部署在客户的内网环境。因为这种情况开发者无法控制客户的网络环境,也不能保证应用所在服务器可以访问外网,因此通常的做法是使用服务器许可文件,在应用启动的时候加载证书,然后在登录或者其他关键操作的地方校验证书的有效性(本文介绍的就是这种)。
  • 应用部署到客户现场,但是客户服务器可以联网,那就可以采用颁发许可证书进行验证,也可以远程认证-认证服务器提供认证请求,必须走线上认证才可使用。

注意:任何加密都有反编译、破解、跳过的手段。

license授权机制的原理
TrueLicense是一个开源的证书管理引擎。

  1. 生成密钥对,使用Keytool生成公私钥证书库。
  2. 授权者保留私钥,使用私钥对包含授权信息(如使用截止日期,MAC地址等)的license进行数字签名。
  3. 公钥给使用者(放在验证的代码中使用),用于验证license是否符合使用条件。

项目源代码地址:https://www.aliyundrive.com/s/2zzpRqTC988

二 实战

2.1 服务端代码

使用Keytool生成公私钥证书库

假如我们设置公钥库密码为:public_password1234,私钥库密码为:private_password1234,则生成命令如下(按顺序执行下边的三条命令即可):

## 1. 生成私匙库
# validity:私钥的有效期多少天
# alias:私钥别称
# keystore: 指定私钥库文件的名称(生成在当前目录)
# storepass:指定私钥库的密码(获取keystore信息所需的密码) 
# keypass:指定别名条目的密码(私钥的密码) 
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"

在D盘新建一个license目录,在该目录下打开cmd窗口,执行上边第一个命令后,就会生成privateKeys.keystore文件,如下
在这里插入图片描述

再接着执行第二条命令,生成certfile.cer文件,如下

在这里插入图片描述
再接着执行第三条命令,生成publicCerts.keystore文件,如下

在这里插入图片描述

上述命令执行完成之后,会在当前路径下生成三个文件,分别是:privateKeys.keystore、publicCerts.keystore、certfile.cer。其中文件certfile.cer不再需要,可以删除,文件privateKeys.keystore用于当前的 license-server 项目给客户生成license文件,而文件publicCerts.keystore则随应用代码部署到客户license-client服务器,用户解密license文件并校验其许可信息。

把server程序部署到客户服务器上,获取客户端的信息
把license-server项目部署到用户服务器上,启动项目里的license-server端程序,然后访问如下接口http://localhost:8000/license/getServerInfos,获取客户部署服务器的cpu、ip、mac地址、主板序列号等等信息。当然,如果license里不校验用户服务器的这些信息的话,就没必要获取这些内容了。如license只对有效日期进行校验,则不需要访问这个接口获取用户的部署环境信息了。
在这里插入图片描述

生成license证书
在我们自己本地(不是客户服务器了),运行license-server项目,项目启动后,由于是post请求,只能通过postman访问接口,生成证书,请求地址:http://localhost:8000/license/generateLicense

头信息

在这里插入图片描述
body信息如下:
ip和mac是数组,可以写多个。licenseCheckModel里的内容,不校验的话可以不写
在这里插入图片描述
body如下

在这里插入图片描述

body具体参数:

{
		"subject": "license_demo",
		"privateAlias": "privateKey",
		"keyPass": "private_password1234",
		"storePass": "public_password1234",
		"licensePath": "D:/license/license.lic",
		"privateKeysStorePath": "D:/license/privateKeys.keystore",
		"issuedTime": "2024-04-10 00:00:01",
		"expiryTime": "2026-05-31 23:59:59",
		"consumerType": "User",
		"consumerAmount": 1,
		"description": "这是证书描述信息",
		"licenseCheckModel": {
			"ipAddress": [],
			"macAddress": [],
			"cpuSerial": "",
			"mainBoardSerial": ""
		}
	}

上边参数,各字段含义如下

{
		"subject": "license_demo",主题名,一般写项目名就行
		"privateAlias": "privateKey",私钥名称
		"keyPass": "private_password1234",私钥密码·
		"storePass": "public_password1234",证书校验的密码
		"licensePath": "D:/license/license.lic",要生成的证书路径
		"privateKeysStorePath": "D:/license/privateKeys.keystore",之前生成的文件的路径
		"issuedTime": "2023-04-10 00:00:01",证书开始生效的时间
		"expiryTime": "2024-05-31 23:59:59",证书有效期结束的时间
		"consumerType": "User",
		"consumerAmount": 1,
		"description": "这是证书描述信息",
		"licenseCheckModel": {  下边这些内容可以为空,为空的话就不进行验证了,有值才会进行验证
			"ipAddress": [],ip地址(由于客户的ip会动态变化所以一般不设置ip校验)
			"macAddress": [],mac地址
			"cpuSerial": "",cpu序列号
			"mainBoardSerial": "" 主板序列号
		}
	}

调完接口后,会生成证书如下

在这里插入图片描述

生成上边的证书,也可以直接执行代码里提供的测试方法:

在这里插入图片描述

2.1 客户端代码验证证书

资料里的license-client项目,就是要部署在客户服务器上的项目。

把2.1章节里生成的license.lic文件,还有公钥文件,放到客户服务器指定目录,项目里指定的目录要与这个文件目录一致(这种方式,放在指定磁盘目录不太友好,第三章节讲解如何放在当前项目的resource目录下即可)
在这里插入图片描述

启动项目测试
在这里插入图片描述

三 之后维护步骤

1、启动license-server,使用postman调http://localhost:8000/license/generateLicense接口,请求参数只修改下边的 issuedTime(不可以早于当前时间) 和expiryTime(证书过期时间)即可,其他参数都不用动了

在这里插入图片描述

  1. 访问完上边的接口后,在D盘tmp目录会生成license.lic和publicCerts.keystore文件,把这俩文件放在客户服务器的对应目录即可,目录具体地址,要与部署在客户服务器上配置文件下边这里配置的地址一样
    在这里插入图片描述

上边的代码,密钥文件必须放在客户磁盘目录,更好的做法是放在项目相对路径,而不是绝对路径,我们把这俩文件放在客户项目的resource/license目录里,如下

在这里插入图片描述

修改代码如下(license-client项目):
CustomKeyStoreParam.java的getStream方法

在这里插入图片描述

public InputStream getStream() throws IOException {
        ClassPathResource resource = new ClassPathResource(storePath);
        InputStream inputStream = null;
        try {
            inputStream = resource.getInputStream();
            if (null == inputStream) {
                throw new FileNotFoundException(storePath);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
      /*  final InputStream in = new FileInputStream(new File(storePath));
        if (null == in) {
            throw new FileNotFoundException(storePath);
        }*/
        return inputStream;
    }

LicenseVerify.java的install方法

在这里插入图片描述

 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();

            // 获取resources下的文件
            ClassPathResource resource = new ClassPathResource(param.getLicensePath());
            File outputFile = null;
            // 或者更推荐使用下面的方式直接获取输入流来读取资源内容
            try (InputStream inputStream = resource.getInputStream()) {
                // 处理inputStream,转为File对象
                outputFile = convertInputStreamToFile(inputStream);
                logger.info("临时文件路径: {}", outputFile.getAbsolutePath());
                // 记得处理完之后删除临时文件,以避免积累无用文件
                outputFile.deleteOnExit();
            } catch (IOException e) {
                e.printStackTrace();
            }

            result = licenseManager.install(outputFile);
            // result = licenseManager.install(new File(param.getLicensePath()));
            logger.info(MessageFormat.format("证书安装成功,证书有效期:{0} - {1}", format.format(result.getNotBefore()), format.format(result.getNotAfter())));
        } catch (Exception e) {
            logger.error("证书安装失败!", e);
        }

        return result;
    }

以后,就直接把license文件放client项目resource/license里,打包运行即可

四 第三方项目引入上边license-client校验

我们开发项目时,可以直接引入上边license-client的依赖,再对第三方项目进行简单配置即可

  1. license-client项目的application.yml加入数据库配置
spring:
  redis:
    # 地址
    host: 192.168.119.132
    # 端口,默认为6379
    port: 6379
  application :
    name : tcs-server-push
  servlet:
    multipart:
      max-file-size: -1
      max-request-size: -1
  datasource:
    url: jdbc:postgresql://192.168.119.132:5432/FDS?useUnicode=true&characterEncoding=UTF-8&useSSL=false&serverTimezone=Hongkong&allowPublicKeyRetrieval=true&allowMultiQueries=true
    username: postgres
    password: ts123456
    driver-class-name: org.postgresql.Driver
    type: com.alibaba.druid.pool.DruidDataSource
    jpa:
      properties:
        hibernate:
          dialect: org.hibernate.dialect.PostgreSQLDialect
          hbm2ddl:
            auto: create
    druid:
      initial-size: 3
      min-idle: 3
      max-active: 10
      max-wait: 2000
      time-between-eviction-runs-millis: 2000
      validation-query: SELECT 1
      test-while-idle: true
      test-on-borrow: false
      test-on-return: false
      filter:
        stat:
          log-slow-sql: true
  1. 把license-client进行maven install到本地仓库
  2. 第三方项目加入以来
<dependency>
    <artifactId>license-client</artifactId>
     <groupId>com.sgw</groupId>
     <version>0.0.1-SNAPSHOT</version>
</dependency>

在第三方项目application.yml加入配置:

license:
  subject: license_demo
  publicAlias: publicCert
  storePass: wyjfd65gj
  licensePath: license/license.lic
  publicKeysStorePath: license/publicCerts.keystore

启动第三方项目即可:

在这里插入图片描述

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

麦芽糖0219

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值