一、整体流程和加解密算法
流程介绍
- 获取硬件特征值:
springboot oshi-->获取硬件原始信息-->sha256Hex加密-->原始硬件特征值-->DES加密-->RSA公钥加密-->加密后的硬件特征值 - 生成license文件
RSA私钥解密-->DES解密-->原始硬件特征值-->设置证书属性-->DES加密-->RSA公钥加密-->生成license - 服务使用license文件
读取license文件-->RSA私钥解密-->DES解密-->获取证书属性-->获取原始硬件特征值-->验证是否一致-->验证签发日期-->验证签名产品-->加载到容器
使用到的加解密算法
- SHA256Hex(加密内容)
- DES加解密 (密钥、加密内容)
- RSA公钥加密、私钥解密 (公钥、私钥、加密内容)
密钥存放位置
存在2对DES密钥放在证书发放服务端和客户服务端。存在2对RSA公私钥,硬件特征值加密的RSA公钥和证书内容解密的RSA私钥存放于客户服务端,硬件特征值解密的RSA私钥和证书内容加密的RSA公钥存放于证书发放服务端。
二、授权码限制策略
-
硬件特征
对比运行服务本地获取到的硬件特征值和证书解密出来的硬件特征值,如果二者不同即认为服务器环境发生变化。
硬件特征主要包括:mac地址、总内存大小、cpu序列号、硬盘名称型号大小、显卡名称标识供应商、主板厂商型号序列号 -
时间限制
存在签发时间和过期时间限制,加载授权码时优先获取网络时间(国家授时中心和产品xx官网),如果可以获取网络时间则当前时间以网络时间为准,否则以本机读取时间为准。如果获取到网络时间,强制校验本机时间与网络时间的时差,绝对值超过1小时抛出异常,无法正常授权。校验当前时间是否在签发日期和结束日期中间,否则无法正常授权。
如果正常加载授权文件,会创建一个定时器任务,在(授权截止日期-当前时间)后触发重载授权文件事件,如果此时读取到的证书仍然和内存中的缓存证书ID相同,则认为此证书过期。在加载证书文件时触发。 -
用户数
用户服务通过读取LicenseArguments自行判断。 -
连接数
quill sheet业务服务通过读取LicenseArguments自行判断。在线文档连接数。 -
文档类型
fs服务通过读取LicenseArguments自行判断,如创建接口。office服务也可考虑集成,统一拦截即可。 -
产品名称
枚举包含产品xx、产品xxOPENAPI、产品xx中台能力,读取服务中的配置与证书配置 对比进行校验。在加载证书文件时触发。
三、starter组件开发
总共有3个项目,分别是证书创建项目、证书starter组件、web集成组件测试项目。
开发license starter组件,用于springboot项目快速集成。
相关类介绍:
LicenseAutoConfiguration stater入口类
SystemTimeUtil 获取网络时间和系统时间,进行时间校验。
LicenseUtil 加载证书获取证书信息,读取硬件设备信息,创建定时器校验时间。
GatewayLicenseFilter 网关过滤器,拦截所有请求,证书缺失或过期时不能正常访问服务接口。
WebLicenseFilter WEB服务过滤器,拦截所有请求,证书缺失或过期时不能正常访问服务接口。
LicenseManageController 管理证书文件的所有接口,用于证书加载、信息查询入口。
相关接口:
/console/license/file 上传授权文件
/console/license/code 上传授权码
/console/license/info 查询加载的授权信息
/console/license/reload 重载本地授权文件
/console/hardware/code 查询硬件特征值
/console/hardware/file 下载硬件特征文件
四、业务服务集成
网关集成
pom.xml
<dependency>
<groupId>com.iflytek</groupId>
<artifactId>iflydocs-license-starter</artifactId>
<version>1.0.1</version>
<exclusions>
<exclusion>
<artifactId>commons-io</artifactId>
<groupId>commons-io</groupId>
</exclusion>
<exclusion>
<artifactId>fastjson</artifactId>
<groupId>com.alibaba</groupId>
</exclusion>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.1.0</version>
</dependency>
application.properties
license.autoconfig.enable=true
license.filter.gateway=true
license.product=IFLYDOCS_OPENAPI
web服务集成
pom.xml
<dependency>
<groupId>com.iflytek</groupId>
<artifactId>iflydocs-license-starter</artifactId>
<version>1.0.1</version>
<exclusions>
<exclusion>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</exclusion>
<exclusion>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-context</artifactId>
</exclusion>
</exclusions>
</dependency>
application.properties
license.autoconfig.enable=true
license.filter.web=true
license.product=IFLYDOCS
pom引入starter组件
license.autoconfig.enable=true #使starter组件生效
license.filter.web=true #使web过滤器生效
license.filter.gateway=true #使gateway过滤器生效
license.product=IFLYDOCS #对证书产品名称进行校验,openapi的证书不
集成iflydocs-license-starter组件的服务,打包注意添加classfinal配置,对lib包进行加密,同时改造了公私钥字符串为方法返回,使其无法反编译。(删除2022.7.12前本地依赖包,从docker仓库中重新拉取。)
<libjars>iflydocs-license-starter-*.jar</libjars>
启动流程:
服务启动时默认加载jar包同级的license.txt文件,如果通过提交授权码接口进行授权,也会在jar包同级目录创建license.txt文件,将授权码写入。
五、在线授权
1.调用接口查询部署服务器的硬件特征值。
curl --location --request GET 'http://demoapi.iflydocs.com/console/hardware/code' \
--header 'Cookie: _wafuid=56176759'
2.通过iflydocs-license-creator的LicenseUtil类生成授权码。
3.获取到授权码后调用服务接口
curl --location --request PUT 'https://demoapi.iflydocs.com/console/license/code' \
--header 'Content-Type: application/json' \
--data-raw '{
"licenseCode":"PT4L3iAdo18/I8/xD9lcElwwYTAQVH01Zzj90oth4dvsxQYtX9AHFrbncOmnTAzQAjgFjh8VvFkhPgJHQxzGikiYT9zGciCeKMPk9c7qFB7nk6aQEFEh1bP3+x5+OnL2A18Xlre5k0DEv9S7MUO10U0zz1ts84KErUyNK7qRu/RGNJdJdwdSanlItJnjN92wML12ot5CLLax4jeWrR0DlSpyi6E0avF/gkaZVWG+RO+SWhz0MZmZKxm35lQyeEESpG/eqIOlA+eklRsp++/0VVcZzuDqSxTM2TpaZmE3VM0fW60gHC/8hbatN/YSp/6xsols6dgjwZTOy5MZMoStCyPX0GU4UpQ+g+KFORf7SasTEgF/wVfy/6FDQ2oAffl8GgGRs2YdyMGvUEqSWi9mcEO/Gupp+Mwm2KSJ6F8zYhaZ6Wvn2s9iiCBxPyTtzFM1VTf27uFTzs2cteAg2oM/6kR6Q53uRSa0aT6t2nQQTxVxzoj3rmqPIWzZontZOWy4BiiiJ6wECH41qZlBwcFxR/ByAwDV8mZ8Vyi3YIg00ueUC7zoudU8fQMVu6oJ70iuSSx8zi0bKeQbGl0C2GzM9VX94nMK8i+QbCq4qvOGqZNrjDf2/5rv1vUCb7ewf0yMNf68Nf0nxZMlFYOo0Be7KiuaW3WOQQ4C/sy7IAE9LdJU0/Jev6rRlFpYXYYb+5PYgDi6GEpiTZvaUO7Hzx28RP6DzCH56ZTpPTJaxqcg+Sw1kCKFCxMooh+IT+V+Tygn9bHJyLj91wx5KUmj9NYTpJGzx9gjhy1neRJMSxnz6m3qGeJC2CP4tCh80E7cjP2NUWRwx5zHtqiij8JyHM7Z71l7PQ0R+3Vhk0h2IOnrDS6vRt7QS1HLf680X6JcIR4Gvb1vXdGyypMVZpqgqGiBfCdBrdGyXLNSCKAZGas2XQcr1Gpfl9kF1PJiq7T/hNNbF9zvbAQ3lSyLrh8qdXnv4m52LLtq5DiJ2hgTvJ4Csk0bNtwkuLvMfcwKRL+v8H9hQaxM1CUcFUk0el35/UV0PnhNvbhv4KqEoudHgSTcTplh/nfXceLIkl4qKNuWetngV1y7dAR7sjJbL6ymmrpIG1DY9EMViMFAw3UQphxQQZUZA0qVq4mLCAHvQcovPgRDHfqAPjYk/9InFY0oZANOkdDbtEHmjiZlC+vDQJ+oXC8="
}'
4.查询授权后的权限
curl --location --request GET 'http://demoapi.iflydocs.com/console/license/info' \
--header 'Cookie: _wafuid=42189409'
该接口读取LicenseArguments
{
"code": 0,
"message": "success",
"data": {
"id": "ec6fb267-9f2e-43a7-9d50-d4cd61f7803d",
"hardwareFeatureCode": "71f16c5532ad34814387c022204eb7495e5b4ed7c32f1b1d1da454da7b5baa7b",
"type": "OFFLINE",
"signDate": 1657444454402,
"maxConnections": 1000,
"maxUsers": 10000,
"supportFunctions": [
"NOTE",
"SHEET",
"SHORTHAND",
"WORD",
"PPT",
"EXCEL",
"FORM"
],
"productTypes": [
"IFLYDOCS"
],
"expirationDate": 1688548454403,
"expire": false
}
}
5.如证书非法、缺失或过期时业务接口统一返回:
{
"code": 50000,
"data": "授权证书缺失或过期,接口无法正常访问。",
"message": "server error"
}