mirror of
https://github.com/continew-org/continew-starter.git
synced 2025-09-08 16:57:09 +08:00
feat(license): 新增 License 模块
This commit is contained in:
@@ -0,0 +1,84 @@
|
|||||||
|
## continew-starter-license-generate 食用方法
|
||||||
|
|
||||||
|
1. 引入依赖
|
||||||
|
|
||||||
|
```java
|
||||||
|
<dependency>
|
||||||
|
<groupId>top.continew</groupId>
|
||||||
|
<artifactId>continew-starter-license-generate</artifactId>
|
||||||
|
<version>2.11.0-SNAPSHOT</version>
|
||||||
|
</dependency>
|
||||||
|
```
|
||||||
|
|
||||||
|
2. 开发对外接口
|
||||||
|
|
||||||
|
```
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2022-present Charles7c Authors. All Rights Reserved.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package top.continew.admin.controller.license;
|
||||||
|
|
||||||
|
import cn.dev33.satoken.annotation.SaIgnore;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.web.bind.annotation.GetMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RequestMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
|
import top.continew.license.dto.LicenseCreatorParamVO;
|
||||||
|
import top.continew.license.dto.LicenseExtraModel;
|
||||||
|
import top.continew.license.service.LicenseCreateService;
|
||||||
|
|
||||||
|
import java.util.Calendar;
|
||||||
|
import java.util.Date;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Desc:
|
||||||
|
* @Author loach
|
||||||
|
* @ClassName top.continew.admin.controller.license.LicenseGenerateController
|
||||||
|
* @Date 2025-04-15 10:52
|
||||||
|
*/
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("/license")
|
||||||
|
public class LicenseGenerateController {
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private LicenseCreateService licenseCreateService;
|
||||||
|
|
||||||
|
@GetMapping("/generate")
|
||||||
|
public void listDict() throws Exception {
|
||||||
|
//设置证书校验参数
|
||||||
|
LicenseCreatorParamVO paramVO = new LicenseCreatorParamVO();
|
||||||
|
paramVO.setCustomerName("continew");
|
||||||
|
paramVO.setDescription("continew");
|
||||||
|
paramVO.setKeyPass("123456a");
|
||||||
|
paramVO.setStorePass("123456a");
|
||||||
|
//设置过期时间
|
||||||
|
Calendar calendar = Calendar.getInstance();
|
||||||
|
long expire = new Date().getTime() + (24L * 3600L * 1000L);
|
||||||
|
calendar.setTimeInMillis(expire);
|
||||||
|
paramVO.setExpireTime(calendar.getTime());
|
||||||
|
//设置额外校验参数(服务器信息)
|
||||||
|
LicenseExtraModel extraModel = licenseCreateService.getServerInfo();
|
||||||
|
paramVO.setLicenseExtraModel(extraModel);
|
||||||
|
licenseCreateService.generateLicense(paramVO);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
注:默认生成 license 为C:/license下。将压缩包发送给客户端使用。
|
@@ -0,0 +1,76 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||||
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
<parent>
|
||||||
|
<groupId>top.continew</groupId>
|
||||||
|
<artifactId>continew-starter-license</artifactId>
|
||||||
|
<version>${revision}</version>
|
||||||
|
</parent>
|
||||||
|
|
||||||
|
<artifactId>continew-starter-license-generate</artifactId>
|
||||||
|
<packaging>jar</packaging>
|
||||||
|
<description>license 生成模块 基于 truelicens 实现</description>
|
||||||
|
|
||||||
|
<properties>
|
||||||
|
<truelicense.version>1.33</truelicense.version>
|
||||||
|
<zip4j.version>2.2.6</zip4j.version>
|
||||||
|
</properties>
|
||||||
|
|
||||||
|
<dependencies>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>junit</groupId>
|
||||||
|
<artifactId>junit</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-test</artifactId>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>top.continew</groupId>
|
||||||
|
<artifactId>continew-starter-log-core</artifactId>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>de.schlichtherle.truelicense</groupId>
|
||||||
|
<artifactId>truelicense-core</artifactId>
|
||||||
|
<version>${truelicense.version}</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<!--zip4j压缩文件-->
|
||||||
|
<dependency>
|
||||||
|
<groupId>net.lingala.zip4j</groupId>
|
||||||
|
<artifactId>zip4j</artifactId>
|
||||||
|
<version>${zip4j.version}</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<!--jackson-->
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.fasterxml.jackson.core</groupId>
|
||||||
|
<artifactId>jackson-databind</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.fasterxml.jackson.core</groupId>
|
||||||
|
<artifactId>jackson-core</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.fasterxml.jackson.core</groupId>
|
||||||
|
<artifactId>jackson-annotations</artifactId>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<!--apache-commons-->
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.apache.commons</groupId>
|
||||||
|
<artifactId>commons-lang3</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-test</artifactId>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
|
</project>
|
@@ -0,0 +1,38 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2022-present Charles7c Authors. All Rights Reserved.
|
||||||
|
* <p>
|
||||||
|
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
* <p>
|
||||||
|
* http://www.gnu.org/licenses/lgpl.html
|
||||||
|
* <p>
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package top.continew.license.autoConfiguration;
|
||||||
|
|
||||||
|
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||||
|
import org.springframework.context.annotation.Bean;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
import top.continew.license.service.LicenseCreateService;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Desc:
|
||||||
|
* @Author loach
|
||||||
|
* @ClassName top.continew.license.AutoConfiguration.LicenseGenerateAutoConfiguration
|
||||||
|
* @Date 2025-03-23 10:57
|
||||||
|
*/
|
||||||
|
@Configuration
|
||||||
|
public class LicenseGenerateAutoConfiguration {
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
@ConditionalOnMissingBean
|
||||||
|
public LicenseCreateService licenseCreateService() {
|
||||||
|
return LicenseCreateService.getInstance();
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,59 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2022-present Charles7c Authors. All Rights Reserved.
|
||||||
|
* <p>
|
||||||
|
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
* <p>
|
||||||
|
* http://www.gnu.org/licenses/lgpl.html
|
||||||
|
* <p>
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package top.continew.license.dto;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Desc:
|
||||||
|
* @Author loach
|
||||||
|
* @ClassName top.continew.license.dto.ConfigParam
|
||||||
|
* @Date 2025-03-21 14:50
|
||||||
|
*/
|
||||||
|
public class ConfigParam {
|
||||||
|
|
||||||
|
/** 主题 */
|
||||||
|
private String subject;
|
||||||
|
|
||||||
|
/** 公钥别称 */
|
||||||
|
private String publicAlias;
|
||||||
|
|
||||||
|
/** 访问公钥库的密码 */
|
||||||
|
private String storePass;
|
||||||
|
|
||||||
|
public String getSubject() {
|
||||||
|
return subject;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSubject(String subject) {
|
||||||
|
this.subject = subject;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getPublicAlias() {
|
||||||
|
return publicAlias;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPublicAlias(String publicAlias) {
|
||||||
|
this.publicAlias = publicAlias;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getStorePass() {
|
||||||
|
return storePass;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setStorePass(String storePass) {
|
||||||
|
this.storePass = storePass;
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,171 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2022-present Charles7c Authors. All Rights Reserved.
|
||||||
|
* <p>
|
||||||
|
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
* <p>
|
||||||
|
* http://www.gnu.org/licenses/lgpl.html
|
||||||
|
* <p>
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package top.continew.license.dto;
|
||||||
|
|
||||||
|
import java.io.Serial;
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.util.Date;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Desc:
|
||||||
|
* @Author loach
|
||||||
|
* @ClassName top.continew.license.dto.LicenseCreatorParam
|
||||||
|
* @Date 2025-03-21 14:22
|
||||||
|
*/
|
||||||
|
public class LicenseCreatorParam implements Serializable {
|
||||||
|
|
||||||
|
@Serial
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
/** 证书主题 */
|
||||||
|
private String subject;
|
||||||
|
|
||||||
|
/** 私钥别称 */
|
||||||
|
private String privateAlias;
|
||||||
|
|
||||||
|
/** 私钥密码 */
|
||||||
|
private String keyPass;
|
||||||
|
|
||||||
|
/** 访问公钥库的密码 */
|
||||||
|
private String storePass;
|
||||||
|
|
||||||
|
/** 证书生成路径 */
|
||||||
|
private String licensePath;
|
||||||
|
|
||||||
|
/** 私钥库存储路径 */
|
||||||
|
private String privateKeysStorePath;
|
||||||
|
|
||||||
|
/** 证书生效时间 */
|
||||||
|
private Date issuedTime = new Date();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 证书失效时间
|
||||||
|
*/
|
||||||
|
private Date expiryTime;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 用户类型
|
||||||
|
*/
|
||||||
|
private String consumerType = "user";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 用户数量
|
||||||
|
*/
|
||||||
|
private Integer consumerAmount = 1;
|
||||||
|
|
||||||
|
/** 描述信息 */
|
||||||
|
private String description;
|
||||||
|
|
||||||
|
/** 额外的服务器硬件校验信息 */
|
||||||
|
private LicenseExtraModel licenseExtraModel;
|
||||||
|
|
||||||
|
public String getSubject() {
|
||||||
|
return subject;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSubject(String subject) {
|
||||||
|
this.subject = subject;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getPrivateAlias() {
|
||||||
|
return privateAlias;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPrivateAlias(String privateAlias) {
|
||||||
|
this.privateAlias = privateAlias;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getKeyPass() {
|
||||||
|
return keyPass;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setKeyPass(String keyPass) {
|
||||||
|
this.keyPass = keyPass;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getStorePass() {
|
||||||
|
return storePass;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setStorePass(String storePass) {
|
||||||
|
this.storePass = storePass;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getLicensePath() {
|
||||||
|
return licensePath;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setLicensePath(String licensePath) {
|
||||||
|
this.licensePath = licensePath;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getPrivateKeysStorePath() {
|
||||||
|
return privateKeysStorePath;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPrivateKeysStorePath(String privateKeysStorePath) {
|
||||||
|
this.privateKeysStorePath = privateKeysStorePath;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Date getIssuedTime() {
|
||||||
|
return issuedTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setIssuedTime(Date issuedTime) {
|
||||||
|
this.issuedTime = issuedTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Date getExpiryTime() {
|
||||||
|
return expiryTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setExpiryTime(Date expiryTime) {
|
||||||
|
this.expiryTime = expiryTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getConsumerType() {
|
||||||
|
return consumerType;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setConsumerType(String consumerType) {
|
||||||
|
this.consumerType = consumerType;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Integer getConsumerAmount() {
|
||||||
|
return consumerAmount;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setConsumerAmount(Integer consumerAmount) {
|
||||||
|
this.consumerAmount = consumerAmount;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getDescription() {
|
||||||
|
return description;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDescription(String description) {
|
||||||
|
this.description = description;
|
||||||
|
}
|
||||||
|
|
||||||
|
public LicenseExtraModel getLicenseExtraModel() {
|
||||||
|
return licenseExtraModel;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setLicenseExtraModel(LicenseExtraModel licenseExtraModel) {
|
||||||
|
this.licenseExtraModel = licenseExtraModel;
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,121 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2022-present Charles7c Authors. All Rights Reserved.
|
||||||
|
* <p>
|
||||||
|
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
* <p>
|
||||||
|
* http://www.gnu.org/licenses/lgpl.html
|
||||||
|
* <p>
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package top.continew.license.dto;
|
||||||
|
|
||||||
|
import java.util.Date;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 为用户生成证书需要的具体参数
|
||||||
|
*
|
||||||
|
* @Desc:
|
||||||
|
* @Author loach
|
||||||
|
* @ClassName top.continew.license.dto.LicenseCreatorParamVO
|
||||||
|
* @Date 2025-03-21 14:29
|
||||||
|
*/
|
||||||
|
public class LicenseCreatorParamVO {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 有效期截至时间
|
||||||
|
*/
|
||||||
|
private Date expireTime;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 客户名称
|
||||||
|
*/
|
||||||
|
private String customerName;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 公钥钥库密码库,必须包含数字和字母
|
||||||
|
*/
|
||||||
|
private String storePass;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 私钥密码,必须包含数字和字母
|
||||||
|
*/
|
||||||
|
private String keyPass;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 描述信息
|
||||||
|
*/
|
||||||
|
private String description;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* license 保存位置
|
||||||
|
*/
|
||||||
|
private String licenseSavePath;
|
||||||
|
|
||||||
|
public String getLicenseSavePath() {
|
||||||
|
return licenseSavePath;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setLicenseSavePath(String licenseSavePath) {
|
||||||
|
this.licenseSavePath = licenseSavePath;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 额外的服务器硬件校验信息
|
||||||
|
*/
|
||||||
|
private LicenseExtraModel licenseExtraModel;
|
||||||
|
|
||||||
|
public Date getExpireTime() {
|
||||||
|
return expireTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setExpireTime(Date expireTime) {
|
||||||
|
this.expireTime = expireTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getCustomerName() {
|
||||||
|
return customerName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setCustomerName(String customerName) {
|
||||||
|
this.customerName = customerName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getStorePass() {
|
||||||
|
return storePass;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setStorePass(String storePass) {
|
||||||
|
this.storePass = storePass;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getKeyPass() {
|
||||||
|
return keyPass;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setKeyPass(String keyPass) {
|
||||||
|
this.keyPass = keyPass;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getDescription() {
|
||||||
|
return description;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDescription(String description) {
|
||||||
|
this.description = description;
|
||||||
|
}
|
||||||
|
|
||||||
|
public LicenseExtraModel getLicenseExtraModel() {
|
||||||
|
return licenseExtraModel;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setLicenseExtraModel(LicenseExtraModel licenseExtraModel) {
|
||||||
|
this.licenseExtraModel = licenseExtraModel;
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,83 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2022-present Charles7c Authors. All Rights Reserved.
|
||||||
|
* <p>
|
||||||
|
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
* <p>
|
||||||
|
* http://www.gnu.org/licenses/lgpl.html
|
||||||
|
* <p>
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package top.continew.license.dto;
|
||||||
|
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 额外的服务器硬件校验信息对象,这里的属性可根据需求自定义
|
||||||
|
*
|
||||||
|
* @Desc:
|
||||||
|
* @Author loach
|
||||||
|
* @ClassName top.continew.license.dto.LicenseExtraModel
|
||||||
|
* @Date 2025-03-21 14:28
|
||||||
|
*/
|
||||||
|
public class LicenseExtraModel {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 可被允许的IP地址
|
||||||
|
*/
|
||||||
|
private Set<String> ipAddress;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 可被允许的mac地址
|
||||||
|
*/
|
||||||
|
private Set<String> macAddress;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 可被允许的CPU序列号
|
||||||
|
*/
|
||||||
|
private String cpuSerial;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 可被允许的主板序列号
|
||||||
|
*/
|
||||||
|
private String mainBoardSerial;
|
||||||
|
|
||||||
|
public Set<String> getIpAddress() {
|
||||||
|
return ipAddress;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setIpAddress(Set<String> ipAddress) {
|
||||||
|
this.ipAddress = ipAddress;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Set<String> getMacAddress() {
|
||||||
|
return macAddress;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setMacAddress(Set<String> macAddress) {
|
||||||
|
this.macAddress = macAddress;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getCpuSerial() {
|
||||||
|
return cpuSerial;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setCpuSerial(String cpuSerial) {
|
||||||
|
this.cpuSerial = cpuSerial;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getMainBoardSerial() {
|
||||||
|
return mainBoardSerial;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setMainBoardSerial(String mainBoardSerial) {
|
||||||
|
this.mainBoardSerial = mainBoardSerial;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@@ -0,0 +1,32 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2022-present Charles7c Authors. All Rights Reserved.
|
||||||
|
* <p>
|
||||||
|
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
* <p>
|
||||||
|
* http://www.gnu.org/licenses/lgpl.html
|
||||||
|
* <p>
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package top.continew.license.exception;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 自定义证书认证异常
|
||||||
|
*
|
||||||
|
* @Desc:
|
||||||
|
* @Author loach
|
||||||
|
* @ClassName top.continew.license.exception.LicenseException
|
||||||
|
* @Date 2025-03-21 14:30
|
||||||
|
*/
|
||||||
|
public class LicenseException extends RuntimeException {
|
||||||
|
|
||||||
|
public LicenseException(String message) {
|
||||||
|
super(message);
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,71 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2022-present Charles7c Authors. All Rights Reserved.
|
||||||
|
* <p>
|
||||||
|
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
* <p>
|
||||||
|
* http://www.gnu.org/licenses/lgpl.html
|
||||||
|
* <p>
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package top.continew.license.keyStoreParam;
|
||||||
|
|
||||||
|
import de.schlichtherle.license.AbstractKeyStoreParam;
|
||||||
|
|
||||||
|
import java.io.FileInputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Desc:
|
||||||
|
* @Author loach
|
||||||
|
* @ClassName top.continew.license.keyStoreParam.CustomKeyStoreParam
|
||||||
|
* @Date 2025-03-21 14:50
|
||||||
|
*/
|
||||||
|
public class CustomKeyStoreParam extends AbstractKeyStoreParam {
|
||||||
|
/** 密钥路径,可为磁盘路径,也可为项目资源文件里的路径,如果为磁盘路径需重写getStream()方法 */
|
||||||
|
private String storePath;
|
||||||
|
|
||||||
|
/** 公钥或私钥的别名 */
|
||||||
|
private String alias;
|
||||||
|
|
||||||
|
/** 访问公钥/私钥库的密码 */
|
||||||
|
private String storePass;
|
||||||
|
|
||||||
|
/** 公钥/私钥的密码 */
|
||||||
|
private String keyPass;
|
||||||
|
|
||||||
|
public CustomKeyStoreParam(Class clazz, String resource, String alias, String storePass, String keyPass) {
|
||||||
|
super(clazz, resource);
|
||||||
|
this.storePath = resource;
|
||||||
|
this.alias = alias;
|
||||||
|
this.storePass = storePass;
|
||||||
|
this.keyPass = keyPass;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getAlias() {
|
||||||
|
return alias;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getStorePwd() {
|
||||||
|
return storePass;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getKeyPwd() {
|
||||||
|
return keyPass;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public InputStream getStream() throws IOException {
|
||||||
|
return new FileInputStream(storePath);
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,70 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2022-present Charles7c Authors. All Rights Reserved.
|
||||||
|
* <p>
|
||||||
|
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
* <p>
|
||||||
|
* http://www.gnu.org/licenses/lgpl.html
|
||||||
|
* <p>
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package top.continew.license.manager;
|
||||||
|
|
||||||
|
import de.schlichtherle.license.*;
|
||||||
|
import de.schlichtherle.xml.GenericCertificate;
|
||||||
|
|
||||||
|
import java.util.Date;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 自定义服务端证书管理类(生成证书)
|
||||||
|
*
|
||||||
|
* @Desc:
|
||||||
|
* @Author loach
|
||||||
|
* @ClassName top.continew.license.manager.ServerLicenseManager
|
||||||
|
* @Date 2025-03-22 14:31
|
||||||
|
*/
|
||||||
|
public class ServerLicenseManager extends LicenseManager {
|
||||||
|
|
||||||
|
public ServerLicenseManager(LicenseParam param) {
|
||||||
|
super(param);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 证书生成参数验证
|
||||||
|
*
|
||||||
|
* @param content
|
||||||
|
* @throws LicenseContentException
|
||||||
|
*/
|
||||||
|
protected synchronized void validateCreate(final LicenseContent content) throws LicenseContentException {
|
||||||
|
Date now = new Date();
|
||||||
|
Date notBefore = content.getNotBefore();
|
||||||
|
Date notAfter = content.getNotAfter();
|
||||||
|
if (notBefore != null && now.before(notBefore)) {
|
||||||
|
throw new LicenseContentException("证书尚未生效,无法生成");
|
||||||
|
}
|
||||||
|
if (notAfter != null && now.after(notAfter)) {
|
||||||
|
throw new LicenseContentException("证书已过期,无法生成");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (notBefore != null && notAfter != null && notBefore.after(notAfter)) {
|
||||||
|
throw new LicenseContentException("证书生效时间晚于失效时间,无法生成");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 重写生成证书的方法,增加生成参数验证
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
protected synchronized byte[] create(LicenseContent content, LicenseNotary notary) throws Exception {
|
||||||
|
initialize(content);
|
||||||
|
validateCreate(content);
|
||||||
|
final GenericCertificate genericCertificate = notary.sign(content);
|
||||||
|
return getPrivacyGuard().cert2key(genericCertificate);
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,310 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2022-present Charles7c Authors. All Rights Reserved.
|
||||||
|
* <p>
|
||||||
|
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
* <p>
|
||||||
|
* http://www.gnu.org/licenses/lgpl.html
|
||||||
|
* <p>
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package top.continew.license.service;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
|
import de.schlichtherle.license.*;
|
||||||
|
import net.lingala.zip4j.ZipFile;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
import top.continew.license.dto.ConfigParam;
|
||||||
|
import top.continew.license.dto.LicenseCreatorParam;
|
||||||
|
import top.continew.license.dto.LicenseCreatorParamVO;
|
||||||
|
import top.continew.license.dto.LicenseExtraModel;
|
||||||
|
import top.continew.license.exception.LicenseException;
|
||||||
|
import top.continew.license.keyStoreParam.CustomKeyStoreParam;
|
||||||
|
import top.continew.license.manager.ServerLicenseManager;
|
||||||
|
import top.continew.license.util.ExecCmdUtil;
|
||||||
|
import top.continew.license.util.ServerInfoUtils;
|
||||||
|
|
||||||
|
import javax.security.auth.x500.X500Principal;
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.*;
|
||||||
|
import java.util.prefs.Preferences;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 证书生成接口 实现类
|
||||||
|
*
|
||||||
|
* @Desc:
|
||||||
|
* @Author loach
|
||||||
|
* @ClassName top.continew.license.service.impl.LicenseCreateServiceImpl
|
||||||
|
* @Date 2025-03-22 18:36
|
||||||
|
*/
|
||||||
|
@Component
|
||||||
|
public class LicenseCreateService {
|
||||||
|
|
||||||
|
private static final Logger log = LoggerFactory.getLogger(LicenseCreateService.class);
|
||||||
|
|
||||||
|
private static volatile LicenseCreateService instance;
|
||||||
|
|
||||||
|
private LicenseCreateService() {
|
||||||
|
// 私有构造
|
||||||
|
}
|
||||||
|
|
||||||
|
public static LicenseCreateService getInstance() {
|
||||||
|
if (instance == null) {
|
||||||
|
synchronized (LicenseCreateService.class) {
|
||||||
|
if (instance == null) {
|
||||||
|
instance = new LicenseCreateService();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final X500Principal DEFAULT_HOLDER_ISSUER = new X500Principal("CN=localhost, OU=localhost, O=localhost, L=SH, ST=SH, C=CN");
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取服务器信息
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public LicenseExtraModel getServerInfo() {
|
||||||
|
LicenseExtraModel serverInfos = ServerInfoUtils.getServerInfos();
|
||||||
|
return serverInfos;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 生成一个证书
|
||||||
|
*
|
||||||
|
* @param paramVO
|
||||||
|
*/
|
||||||
|
public void generateLicense(LicenseCreatorParamVO paramVO) throws Exception {
|
||||||
|
|
||||||
|
Map<String, Object> map = buildCreator(paramVO);
|
||||||
|
LicenseCreatorParam param = (LicenseCreatorParam)map.get("creator");
|
||||||
|
ZipFile clientZipFile = (ZipFile)map.get("clientZipFile");
|
||||||
|
try {
|
||||||
|
LicenseParam licenseParam = initLicenseParam(param);
|
||||||
|
LicenseManager licenseManager = new ServerLicenseManager(licenseParam);
|
||||||
|
LicenseContent licenseContent = initLcenseContent(param);
|
||||||
|
licenseManager.store(licenseContent, new File(param.getLicensePath()));
|
||||||
|
log.info("{}证书生成成功", param.getSubject());
|
||||||
|
clientZipFile.addFile(param.getLicensePath());
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
// log.error("{}生成证书失败:", param.getSubject());
|
||||||
|
throw new LicenseException("生成证书失败:" + param.getSubject());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 封装证书生成参数
|
||||||
|
*/
|
||||||
|
private Map<String, Object> buildCreator(LicenseCreatorParamVO paramVO) throws Exception {
|
||||||
|
String customerName = paramVO.getCustomerName();
|
||||||
|
String privateAlias = customerName + "-private-alias";
|
||||||
|
String publicAlias = customerName + "-public-alias";
|
||||||
|
String relativePath = relativePath(paramVO);
|
||||||
|
String currentCustomerDir = relativePath + customerName + uuid() + "/";
|
||||||
|
File file = new File(currentCustomerDir);
|
||||||
|
if (!file.exists()) {
|
||||||
|
file.mkdirs();
|
||||||
|
}
|
||||||
|
String privateKeystore = currentCustomerDir + "privateKeys.keystore";
|
||||||
|
String publicKeystore = currentCustomerDir + "publicCerts.keystore";
|
||||||
|
|
||||||
|
LicenseCreatorParam param = new LicenseCreatorParam();
|
||||||
|
param.setSubject(customerName);
|
||||||
|
param.setPrivateAlias(privateAlias);
|
||||||
|
param.setKeyPass(paramVO.getKeyPass());
|
||||||
|
param.setStorePass(paramVO.getStorePass());
|
||||||
|
param.setLicensePath(currentCustomerDir + "license.lic");
|
||||||
|
param.setPrivateKeysStorePath(privateKeystore);
|
||||||
|
param.setExpiryTime(paramVO.getExpireTime());
|
||||||
|
param.setDescription(paramVO.getDescription());
|
||||||
|
param.setLicenseExtraModel(paramVO.getLicenseExtraModel());
|
||||||
|
|
||||||
|
if (checkJavaVersion()) {
|
||||||
|
// JDK>=17 生成私钥库
|
||||||
|
|
||||||
|
String exe1 = "keytool -genkeypair -keyalg DSA -keysize 1024 -validity " + getValidity(param
|
||||||
|
.getIssuedTime(), paramVO
|
||||||
|
.getExpireTime()) + " -alias " + privateAlias + " -keystore " + privateKeystore + " -storepass " + paramVO
|
||||||
|
.getStorePass() + " -keypass " + paramVO
|
||||||
|
.getKeyPass() + " -dname \"CN=localhost, OU=localhost, O=localhost, L=SH, ST=SH, C=CN\"";
|
||||||
|
String exe2 = "keytool -exportcert -alias " + privateAlias + " -keystore " + privateKeystore + " -storepass " + paramVO
|
||||||
|
.getStorePass() + " -file \"" + currentCustomerDir + "certfile.cer\"";
|
||||||
|
|
||||||
|
String exe3 = "keytool -noprompt -import -alias " + publicAlias + " -file \"" + currentCustomerDir + "certfile.cer\"" + " -keystore " + publicKeystore + " -storepass " + paramVO
|
||||||
|
.getStorePass();
|
||||||
|
|
||||||
|
ExecCmdUtil.exec(exe1);
|
||||||
|
ExecCmdUtil.exec(exe2);
|
||||||
|
ExecCmdUtil.exec(exe3);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
// JDK<17 生成私钥库
|
||||||
|
String exe1 = "keytool -genkeypair -keysize 1024 -validity " + getValidity(param.getIssuedTime(), paramVO
|
||||||
|
.getExpireTime()) + " -alias " + privateAlias + " -keystore " + privateKeystore + " -storepass " + paramVO
|
||||||
|
.getStorePass() + " -keypass " + paramVO
|
||||||
|
.getKeyPass() + " -dname \"CN=localhost, OU=localhost, " + "O=localhost, L=SH, ST=SH, C=CN\"";
|
||||||
|
String exe2 = "keytool -exportcert -alias " + privateAlias + " -keystore " + privateKeystore + " -storepass " + paramVO
|
||||||
|
.getStorePass() + " -file \"" + currentCustomerDir + "certfile.cer\"";
|
||||||
|
String exe3 = "keytool -noprompt -import -alias " + publicAlias + " -file \"" + currentCustomerDir + "certfile.cer\" -keystore " + publicKeystore + " -storepass " + paramVO
|
||||||
|
.getStorePass();
|
||||||
|
ExecCmdUtil.exec(exe1);
|
||||||
|
ExecCmdUtil.exec(exe2);
|
||||||
|
ExecCmdUtil.exec(exe3);
|
||||||
|
}
|
||||||
|
|
||||||
|
ZipFile clientZipFile = generateClientConfig(param, currentCustomerDir, publicAlias);
|
||||||
|
Map<String, Object> map = new HashMap<>();
|
||||||
|
map.put("creator", param);
|
||||||
|
map.put("clientZipFile", clientZipFile);
|
||||||
|
return map;
|
||||||
|
}
|
||||||
|
|
||||||
|
private String uuid() {
|
||||||
|
return UUID.randomUUID().toString().replace("-", "");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 校验JDK版本
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
* @throws Exception
|
||||||
|
*/
|
||||||
|
private boolean checkJavaVersion() throws Exception {
|
||||||
|
String version = System.getProperty("java.version");
|
||||||
|
|
||||||
|
int currentVersion = 0;
|
||||||
|
if (version.startsWith("1.")) {
|
||||||
|
currentVersion = Integer.parseInt(version.split("\\.")[1]);
|
||||||
|
} else {
|
||||||
|
currentVersion = Integer.parseInt(version.split("\\.")[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (currentVersion >= 17) {
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private ZipFile generateClientConfig(LicenseCreatorParam param,
|
||||||
|
String currentCustomerDir,
|
||||||
|
String publicAlias) throws Exception {
|
||||||
|
|
||||||
|
ZipFile clientLicense = new ZipFile(currentCustomerDir + "clientLicense.zip");
|
||||||
|
File config = new File(currentCustomerDir + "clientConfig.json");
|
||||||
|
ConfigParam configParam = new ConfigParam();
|
||||||
|
configParam.setPublicAlias(publicAlias);
|
||||||
|
configParam.setStorePass(param.getStorePass());
|
||||||
|
configParam.setSubject(param.getSubject());
|
||||||
|
ObjectMapper mapper = new ObjectMapper();
|
||||||
|
String json = mapper.writeValueAsString(configParam);
|
||||||
|
FileOutputStream out = null;
|
||||||
|
try {
|
||||||
|
out = new FileOutputStream(config);
|
||||||
|
out.write(json.getBytes("UTF-8"));
|
||||||
|
out.flush();
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
throw new LicenseException("密钥文件生成失败");
|
||||||
|
} finally {
|
||||||
|
if (out != null) {
|
||||||
|
try {
|
||||||
|
out.close();
|
||||||
|
} catch (IOException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
List<File> files = new ArrayList<>();
|
||||||
|
files.add(config);
|
||||||
|
files.add(new File(currentCustomerDir + "publicCerts.keystore"));
|
||||||
|
clientLicense.addFiles(files);
|
||||||
|
return clientLicense;
|
||||||
|
}
|
||||||
|
|
||||||
|
//将有效时间转换成天
|
||||||
|
private int getValidity(Date issuedTime, Date expireTime) {
|
||||||
|
long issued = issuedTime.getTime();
|
||||||
|
long expire = expireTime.getTime();
|
||||||
|
long differ = expire - issued;
|
||||||
|
long remaining = differ % (24L * 3600L * 1000L);
|
||||||
|
Long validity = differ / (24L * 3600L * 1000L);
|
||||||
|
if (remaining > 0) {
|
||||||
|
validity++;
|
||||||
|
}
|
||||||
|
return validity.intValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isWindows() {
|
||||||
|
String os = System.getProperty("os.name");
|
||||||
|
if (os.toLowerCase().contains("windows")) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
//证书生成路径
|
||||||
|
private String relativePath(LicenseCreatorParamVO paramVO) {
|
||||||
|
|
||||||
|
if (paramVO.getLicenseSavePath() != null) {
|
||||||
|
return paramVO.getLicenseSavePath();
|
||||||
|
}
|
||||||
|
if (isWindows()) {
|
||||||
|
return "C:/license/";
|
||||||
|
}
|
||||||
|
return "/data/license/";
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置证书生成参数
|
||||||
|
*/
|
||||||
|
private LicenseParam initLicenseParam(LicenseCreatorParam param) {
|
||||||
|
Preferences preferences = Preferences.userNodeForPackage(LicenseCreateService.class);
|
||||||
|
//设置密钥
|
||||||
|
CipherParam cipherParam = new DefaultCipherParam(param.getStorePass());
|
||||||
|
KeyStoreParam privateStoreParam = new CustomKeyStoreParam(LicenseCreateService.class, param
|
||||||
|
.getPrivateKeysStorePath(), param.getPrivateAlias(), param.getStorePass(), param.getKeyPass());
|
||||||
|
LicenseParam licenseParam = new DefaultLicenseParam(param
|
||||||
|
.getSubject(), preferences, privateStoreParam, cipherParam);
|
||||||
|
return licenseParam;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置证书生成内容
|
||||||
|
*/
|
||||||
|
private LicenseContent initLcenseContent(LicenseCreatorParam param) {
|
||||||
|
|
||||||
|
LicenseContent licenseContent = new LicenseContent();
|
||||||
|
licenseContent.setHolder(DEFAULT_HOLDER_ISSUER);
|
||||||
|
licenseContent.setIssuer(DEFAULT_HOLDER_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());
|
||||||
|
|
||||||
|
if (param != null && param.getLicenseExtraModel() != null) {
|
||||||
|
licenseContent.setExtra(param.getLicenseExtraModel());
|
||||||
|
}
|
||||||
|
|
||||||
|
return licenseContent;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@@ -0,0 +1,84 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2022-present Charles7c Authors. All Rights Reserved.
|
||||||
|
* <p>
|
||||||
|
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
* <p>
|
||||||
|
* http://www.gnu.org/licenses/lgpl.html
|
||||||
|
* <p>
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package top.continew.license.util;
|
||||||
|
|
||||||
|
import org.apache.commons.lang3.ArrayUtils;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.InputStreamReader;
|
||||||
|
import java.io.LineNumberReader;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 运行命令行工具类
|
||||||
|
*
|
||||||
|
* @Desc:
|
||||||
|
* @Author loach
|
||||||
|
* @ClassName top.continew.license.util.ExecCmdUtil
|
||||||
|
* @Date 2025-03-22 18:44
|
||||||
|
*/
|
||||||
|
public class ExecCmdUtil {
|
||||||
|
|
||||||
|
private static final String CREATE_3RDSESSION_SHELL_SCRIPT = "head -n 80 /dev/urandom | tr -dc A-Za-z0-9 | head -c 168";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 执行cmd命令(shell脚本)
|
||||||
|
*
|
||||||
|
* @param cmd linux sh/windows bat命令
|
||||||
|
* @return String 返回打印信息
|
||||||
|
*/
|
||||||
|
public static String exec(String... cmd) throws IOException {
|
||||||
|
Process process = null;
|
||||||
|
if (System.getProperty("os.name").indexOf("Windows") != -1) {
|
||||||
|
if (cmd != null && cmd.length == 1) {
|
||||||
|
process = Runtime.getRuntime().exec(cmd[0]);
|
||||||
|
} else {
|
||||||
|
process = Runtime.getRuntime().exec(cmd);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
cmd = ArrayUtils.addAll(new String[] {"/bin/sh", "-c"}, cmd);
|
||||||
|
process = Runtime.getRuntime().exec(cmd);
|
||||||
|
}
|
||||||
|
|
||||||
|
String print = readProcess(process.getInputStream());
|
||||||
|
String err = readProcess(process.getErrorStream());
|
||||||
|
return print + " " + err;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String readProcess(InputStream in) {
|
||||||
|
try (LineNumberReader print = new LineNumberReader(new InputStreamReader(in, "GBK"))) {
|
||||||
|
StringBuffer sb = new StringBuffer();
|
||||||
|
String line;
|
||||||
|
while ((line = print.readLine()) != null) {
|
||||||
|
sb.append(line);
|
||||||
|
}
|
||||||
|
return sb.toString();
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 执行linux命令(shell脚本)生成3rd_session随机数
|
||||||
|
*/
|
||||||
|
public static String create3rdSessionToken() throws IOException {
|
||||||
|
|
||||||
|
return exec(CREATE_3RDSESSION_SHELL_SCRIPT);
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,338 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2022-present Charles7c Authors. All Rights Reserved.
|
||||||
|
* <p>
|
||||||
|
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
* <p>
|
||||||
|
* http://www.gnu.org/licenses/lgpl.html
|
||||||
|
* <p>
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package top.continew.license.util;
|
||||||
|
|
||||||
|
import cn.hutool.core.collection.CollUtil;
|
||||||
|
import cn.hutool.core.collection.CollectionUtil;
|
||||||
|
import cn.hutool.core.io.FileUtil;
|
||||||
|
import cn.hutool.core.io.IoUtil;
|
||||||
|
import cn.hutool.core.util.StrUtil;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
import top.continew.license.dto.LicenseExtraModel;
|
||||||
|
import top.continew.license.exception.LicenseException;
|
||||||
|
|
||||||
|
import java.io.*;
|
||||||
|
import java.net.InetAddress;
|
||||||
|
import java.net.NetworkInterface;
|
||||||
|
import java.net.SocketException;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.Enumeration;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 服务器信息工具类
|
||||||
|
*
|
||||||
|
* @author Rong.Jia
|
||||||
|
* @date 2022/03/10
|
||||||
|
*/
|
||||||
|
public class ServerInfoUtils {
|
||||||
|
|
||||||
|
private static final Logger log = LoggerFactory.getLogger(ServerInfoUtils.class);
|
||||||
|
|
||||||
|
private static class ServerInfosContainer {
|
||||||
|
private static Set<String> ipAddress = null;
|
||||||
|
private static Set<String> macAddress = null;
|
||||||
|
private static String cpuSerial = null;
|
||||||
|
private static String mainBoardSerial = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 组装需要额外校验的License参数
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public static LicenseExtraModel getServerInfos() {
|
||||||
|
LicenseExtraModel result = new LicenseExtraModel();
|
||||||
|
try {
|
||||||
|
initServerInfos();
|
||||||
|
result.setIpAddress(ServerInfosContainer.ipAddress);
|
||||||
|
result.setMacAddress(ServerInfosContainer.macAddress);
|
||||||
|
result.setCpuSerial(ServerInfosContainer.cpuSerial);
|
||||||
|
result.setMainBoardSerial(ServerInfosContainer.mainBoardSerial);
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("获取服务器硬件信息异常", e);
|
||||||
|
throw new LicenseException(String.format("获取服务器硬件信息异常, %s", e.getMessage()));
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 初始化服务器硬件信息,并将信息缓存到内存
|
||||||
|
*
|
||||||
|
* @throws Exception 默认异常
|
||||||
|
*/
|
||||||
|
private static void initServerInfos() throws Exception {
|
||||||
|
if (ServerInfosContainer.ipAddress == null) {
|
||||||
|
ServerInfosContainer.ipAddress = getIpAddress();
|
||||||
|
}
|
||||||
|
if (ServerInfosContainer.macAddress == null) {
|
||||||
|
ServerInfosContainer.macAddress = getMacAddress();
|
||||||
|
}
|
||||||
|
if (ServerInfosContainer.cpuSerial == null) {
|
||||||
|
ServerInfosContainer.cpuSerial = getCpuSerial();
|
||||||
|
}
|
||||||
|
if (ServerInfosContainer.mainBoardSerial == null) {
|
||||||
|
ServerInfosContainer.mainBoardSerial = getMainBoardSerial();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取服务器临时磁盘位置
|
||||||
|
*
|
||||||
|
* @return {@link String}
|
||||||
|
*/
|
||||||
|
public static String getServerTempPath() {
|
||||||
|
return System.getProperty("user.dir");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取CPU序列号
|
||||||
|
*
|
||||||
|
* @return String 主板序列号
|
||||||
|
*/
|
||||||
|
public static String getCpuSerial() {
|
||||||
|
return FileUtil.isWindows() ? getWindowCpuSerial() : getLinuxCpuSerial();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取主板序列号
|
||||||
|
*
|
||||||
|
* @return String 主板序列号
|
||||||
|
*/
|
||||||
|
public static String getMainBoardSerial() {
|
||||||
|
return FileUtil.isWindows() ? getWindowMainBoardSerial() : getLinuxMainBoardSerial();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取linux cpu 序列号
|
||||||
|
*
|
||||||
|
* @return {@link String}
|
||||||
|
*/
|
||||||
|
private static String getLinuxCpuSerial() {
|
||||||
|
String result = StrUtil.EMPTY;
|
||||||
|
String cpuIdCmd = "dmidecode";
|
||||||
|
BufferedReader bufferedReader = null;
|
||||||
|
try {
|
||||||
|
// 管道
|
||||||
|
Process p = Runtime.getRuntime().exec(new String[] {"sh", "-c", cpuIdCmd});
|
||||||
|
bufferedReader = new BufferedReader(new InputStreamReader(p.getInputStream()));
|
||||||
|
String line = null;
|
||||||
|
int index = -1;
|
||||||
|
while ((line = bufferedReader.readLine()) != null) {
|
||||||
|
// 寻找标示字符串[hwaddr]
|
||||||
|
index = line.toLowerCase().indexOf("uuid");
|
||||||
|
if (index >= 0) {
|
||||||
|
// 取出mac地址并去除2边空格
|
||||||
|
result = line.substring(index + "uuid".length() + 1).trim();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (IOException e) {
|
||||||
|
log.error("获取Linux cpu信息错误 {}", e.getMessage());
|
||||||
|
} finally {
|
||||||
|
IoUtil.close(bufferedReader);
|
||||||
|
}
|
||||||
|
return result.trim();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取Window cpu 序列号
|
||||||
|
*
|
||||||
|
* @return {@link String}
|
||||||
|
*/
|
||||||
|
private static String getWindowCpuSerial() {
|
||||||
|
|
||||||
|
String result = StrUtil.EMPTY;
|
||||||
|
File file = null;
|
||||||
|
BufferedReader input = null;
|
||||||
|
try {
|
||||||
|
file = File.createTempFile("tmp", ".vbs");
|
||||||
|
file.deleteOnExit();
|
||||||
|
FileWriter fw = new FileWriter(file);
|
||||||
|
String vbs = "Set objWMIService = GetObject(\"winmgmts:\\\\.\\root\\cimv2\")\n" + "Set colItems = objWMIService.ExecQuery _ \n" + " (\"Select * from Win32_Processor\") \n" + "For Each objItem in colItems \n" + " Wscript.Echo objItem.ProcessorId \n" + " exit for ' do the first cpu only! \n" + "Next \n";
|
||||||
|
|
||||||
|
fw.write(vbs);
|
||||||
|
fw.close();
|
||||||
|
|
||||||
|
Process p = Runtime.getRuntime().exec("cscript //NoLogo " + file.getPath());
|
||||||
|
input = new BufferedReader(new InputStreamReader(p.getInputStream()));
|
||||||
|
String line;
|
||||||
|
while ((line = input.readLine()) != null) {
|
||||||
|
result += line;
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("获取window cpu信息错误, {}", e.getMessage());
|
||||||
|
} finally {
|
||||||
|
IoUtil.close(input);
|
||||||
|
FileUtil.del(file);
|
||||||
|
}
|
||||||
|
return result.trim();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取Linux主板序列号
|
||||||
|
*
|
||||||
|
* @return {@link String}
|
||||||
|
*/
|
||||||
|
private static String getLinuxMainBoardSerial() {
|
||||||
|
String result = StrUtil.EMPTY;
|
||||||
|
String maniBordCmd = "dmidecode | grep 'Serial Number' | awk '{print $3}' | tail -1";
|
||||||
|
BufferedReader br = null;
|
||||||
|
try {
|
||||||
|
Process p = Runtime.getRuntime().exec(new String[] {"sh", "-c", maniBordCmd});
|
||||||
|
br = new BufferedReader(new InputStreamReader(p.getInputStream()));
|
||||||
|
String line;
|
||||||
|
while ((line = br.readLine()) != null) {
|
||||||
|
result += line;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} catch (IOException e) {
|
||||||
|
log.error("获取Linux主板信息错误 {}", e.getMessage());
|
||||||
|
} finally {
|
||||||
|
IoUtil.close(br);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取window主板序列号
|
||||||
|
*
|
||||||
|
* @return {@link String}
|
||||||
|
*/
|
||||||
|
private static String getWindowMainBoardSerial() {
|
||||||
|
String result = StrUtil.EMPTY;
|
||||||
|
File file = null;
|
||||||
|
BufferedReader input = null;
|
||||||
|
try {
|
||||||
|
file = File.createTempFile("realhowto", ".vbs");
|
||||||
|
file.deleteOnExit();
|
||||||
|
FileWriter fw = new FileWriter(file);
|
||||||
|
|
||||||
|
String vbs = "Set objWMIService = GetObject(\"winmgmts:\\\\.\\root\\cimv2\")\n" + "Set colItems = objWMIService.ExecQuery _ \n" + " (\"Select * from Win32_BaseBoard\") \n" + "For Each objItem in colItems \n" + " Wscript.Echo objItem.SerialNumber \n" + " exit for ' do the first cpu only! \n" + "Next \n";
|
||||||
|
|
||||||
|
fw.write(vbs);
|
||||||
|
fw.close();
|
||||||
|
Process p = Runtime.getRuntime().exec("cscript //NoLogo " + file.getPath());
|
||||||
|
input = new BufferedReader(new InputStreamReader(p.getInputStream()));
|
||||||
|
String line;
|
||||||
|
while ((line = input.readLine()) != null) {
|
||||||
|
result += line;
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("获取Window主板信息错误 {}", e.getMessage());
|
||||||
|
} finally {
|
||||||
|
IoUtil.close(input);
|
||||||
|
FileUtil.del(file);
|
||||||
|
}
|
||||||
|
return result.trim();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>获取Mac地址</p>
|
||||||
|
*
|
||||||
|
* @return List<String> Mac地址
|
||||||
|
* @throws Exception 默认异常
|
||||||
|
*/
|
||||||
|
public static Set<String> getMacAddress() throws Exception {
|
||||||
|
// 获取所有网络接口
|
||||||
|
Set<InetAddress> inetAddresses = getLocalAllInetAddress();
|
||||||
|
if (CollectionUtil.isNotEmpty(inetAddresses)) {
|
||||||
|
return inetAddresses.stream()
|
||||||
|
.map(ServerInfoUtils::getMacByInetAddress)
|
||||||
|
.distinct()
|
||||||
|
.collect(Collectors.toSet());
|
||||||
|
}
|
||||||
|
return Collections.emptySet();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>获取IP地址</p>
|
||||||
|
*
|
||||||
|
* @return List<String> IP地址
|
||||||
|
* @throws Exception 默认异常
|
||||||
|
*/
|
||||||
|
public static Set<String> getIpAddress() throws Exception {
|
||||||
|
// 获取所有网络接口
|
||||||
|
Set<InetAddress> inetAddresses = getLocalAllInetAddress();
|
||||||
|
if (CollectionUtil.isNotEmpty(inetAddresses)) {
|
||||||
|
return inetAddresses.stream()
|
||||||
|
.map(InetAddress::getHostAddress)
|
||||||
|
.distinct()
|
||||||
|
.map(String::toLowerCase)
|
||||||
|
.collect(Collectors.toSet());
|
||||||
|
}
|
||||||
|
return Collections.emptySet();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>获取某个网络地址对应的Mac地址</p>
|
||||||
|
*
|
||||||
|
* @param inetAddr 网络地址
|
||||||
|
* @return String Mac地址
|
||||||
|
*/
|
||||||
|
private static String getMacByInetAddress(InetAddress inetAddr) {
|
||||||
|
try {
|
||||||
|
byte[] mac = NetworkInterface.getByInetAddress(inetAddr).getHardwareAddress();
|
||||||
|
StringBuilder stringBuilder = new StringBuilder();
|
||||||
|
for (int i = 0; i < mac.length; i++) {
|
||||||
|
if (i != 0) {
|
||||||
|
stringBuilder.append("-");
|
||||||
|
}
|
||||||
|
// 将十六进制byte转化为字符串
|
||||||
|
String temp = Integer.toHexString(mac[i] & 0xff);
|
||||||
|
if (temp.length() == 1) {
|
||||||
|
stringBuilder.append("0").append(temp);
|
||||||
|
} else {
|
||||||
|
stringBuilder.append(temp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return stringBuilder.toString().toUpperCase();
|
||||||
|
} catch (SocketException e) {
|
||||||
|
log.error("getMacByInetAddress {}", e.getMessage());
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>获取当前服务器所有符合条件的网络地址</p>
|
||||||
|
*
|
||||||
|
* @return List<InetAddress> 网络地址列表
|
||||||
|
* @throws Exception 默认异常
|
||||||
|
*/
|
||||||
|
private static Set<InetAddress> getLocalAllInetAddress() throws Exception {
|
||||||
|
|
||||||
|
Set<InetAddress> result = CollUtil.newHashSet();
|
||||||
|
// 遍历所有的网络接口
|
||||||
|
for (Enumeration<NetworkInterface> networkInterfaces = NetworkInterface.getNetworkInterfaces();
|
||||||
|
networkInterfaces.hasMoreElements();) {
|
||||||
|
NetworkInterface ni = networkInterfaces.nextElement();
|
||||||
|
// 在所有的接口下再遍历IP
|
||||||
|
for (Enumeration<InetAddress> addresses = ni.getInetAddresses(); addresses.hasMoreElements();) {
|
||||||
|
InetAddress address = addresses.nextElement();
|
||||||
|
//排除LoopbackAddress、SiteLocalAddress、LinkLocalAddress、MulticastAddress类型的IP地址
|
||||||
|
/*&& !inetAddr.isSiteLocalAddress()*/
|
||||||
|
if (!address.isLoopbackAddress() && !address.isLinkLocalAddress() && !address.isMulticastAddress()) {
|
||||||
|
result.add(address);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@@ -0,0 +1 @@
|
|||||||
|
top.continew.license.autoConfiguration.LicenseGenerateAutoConfiguration
|
@@ -0,0 +1,22 @@
|
|||||||
|
## continew-starter-license-verify 食用方法
|
||||||
|
|
||||||
|
1. 引入依赖
|
||||||
|
|
||||||
|
```java
|
||||||
|
<dependency>
|
||||||
|
<groupId>top.continew</groupId>
|
||||||
|
<artifactId>continew-starter-license-verify</artifactId>
|
||||||
|
<version>2.11.0-SNAPSHOT</version>
|
||||||
|
</dependency>
|
||||||
|
```
|
||||||
|
|
||||||
|
2. 配置YML(license 压缩包存放位置)
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
license:
|
||||||
|
savePath: D:/license/
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
注:默认加载 `D:/license/` 位置。
|
@@ -0,0 +1,66 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||||
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
<parent>
|
||||||
|
<groupId>top.continew</groupId>
|
||||||
|
<artifactId>continew-starter-license</artifactId>
|
||||||
|
<version>${revision}</version>
|
||||||
|
</parent>
|
||||||
|
|
||||||
|
<artifactId>continew-starter-license-verify</artifactId>
|
||||||
|
<packaging>jar</packaging>
|
||||||
|
<description>license 校验模块 基于 truelicens 实现</description>
|
||||||
|
|
||||||
|
<properties>
|
||||||
|
<truelicense.version>1.33</truelicense.version>
|
||||||
|
<zip4j.version>2.2.6</zip4j.version>
|
||||||
|
</properties>
|
||||||
|
|
||||||
|
<dependencies>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>junit</groupId>
|
||||||
|
<artifactId>junit</artifactId>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>top.continew</groupId>
|
||||||
|
<artifactId>continew-starter-log-core</artifactId>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter</artifactId>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>de.schlichtherle.truelicense</groupId>
|
||||||
|
<artifactId>truelicense-core</artifactId>
|
||||||
|
<version>${truelicense.version}</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<!--zip4j压缩文件-->
|
||||||
|
<dependency>
|
||||||
|
<groupId>net.lingala.zip4j</groupId>
|
||||||
|
<artifactId>zip4j</artifactId>
|
||||||
|
<version>${zip4j.version}</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<!--jackson-->
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.fasterxml.jackson.core</groupId>
|
||||||
|
<artifactId>jackson-databind</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.fasterxml.jackson.core</groupId>
|
||||||
|
<artifactId>jackson-core</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.fasterxml.jackson.core</groupId>
|
||||||
|
<artifactId>jackson-annotations</artifactId>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
|
</project>
|
@@ -0,0 +1,47 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2022-present Charles7c Authors. All Rights Reserved.
|
||||||
|
* <p>
|
||||||
|
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
* <p>
|
||||||
|
* http://www.gnu.org/licenses/lgpl.html
|
||||||
|
* <p>
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package top.continew.license.Initializing;
|
||||||
|
|
||||||
|
import org.springframework.beans.factory.InitializingBean;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
import top.continew.license.bean.LicenseInstallerBean;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 启动校验 License
|
||||||
|
*
|
||||||
|
* @Desc:
|
||||||
|
* @Author loach
|
||||||
|
* @ClassName top.continew.license.Initializing.LicenseStarterAutoConfiguration
|
||||||
|
* @Date 2025-04-11 15:40
|
||||||
|
*/
|
||||||
|
@Configuration
|
||||||
|
@EnableConfigurationProperties(LicenseStarterInitializingBean.class)
|
||||||
|
public class LicenseStarterInitializingBean implements InitializingBean {
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private LicenseInstallerBean licenseInstallerBean;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void afterPropertiesSet() throws Exception {
|
||||||
|
|
||||||
|
//安装证书,即校验客户机器参数是否符合证书要求,符合则安装成功,不符合则报错无法启动。
|
||||||
|
licenseInstallerBean.installLicense();
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,104 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2022-present Charles7c Authors. All Rights Reserved.
|
||||||
|
* <p>
|
||||||
|
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
* <p>
|
||||||
|
* http://www.gnu.org/licenses/lgpl.html
|
||||||
|
* <p>
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package top.continew.license.bean;
|
||||||
|
|
||||||
|
import de.schlichtherle.license.*;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
import top.continew.license.config.LicenseVerifyProperties;
|
||||||
|
import top.continew.license.exception.VerifyException;
|
||||||
|
import top.continew.license.manager.CustomLicenseManager;
|
||||||
|
|
||||||
|
import java.io.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 证书安装业务类
|
||||||
|
*
|
||||||
|
* @Desc:
|
||||||
|
* @Author loach
|
||||||
|
* @ClassName top.continew.license.bean.LicenseInstallerBean
|
||||||
|
* @Date 2025-04-15 15:05
|
||||||
|
*/
|
||||||
|
public class LicenseInstallerBean {
|
||||||
|
|
||||||
|
private static final Logger log = LoggerFactory.getLogger(LicenseInstallerBean.class);
|
||||||
|
|
||||||
|
private String licensePath;
|
||||||
|
|
||||||
|
private LicenseManager licenseManager;
|
||||||
|
|
||||||
|
private LicenseVerifyProperties properties;
|
||||||
|
|
||||||
|
public LicenseInstallerBean(LicenseVerifyProperties properties) {
|
||||||
|
this.properties = properties;
|
||||||
|
|
||||||
|
if (properties == null || properties.getSavePath() == null) {
|
||||||
|
String os = System.getProperty("os.name");
|
||||||
|
if (os.toLowerCase().contains("windows")) {
|
||||||
|
this.licensePath = "D:/license/";
|
||||||
|
}
|
||||||
|
this.licensePath = "/data/license/";
|
||||||
|
} else {
|
||||||
|
this.licensePath = properties.getSavePath();
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//安装证书
|
||||||
|
public void installLicense() throws Exception {
|
||||||
|
|
||||||
|
try {
|
||||||
|
|
||||||
|
licenseManager = CustomLicenseManager.getInstance(properties);
|
||||||
|
licenseManager.uninstall();
|
||||||
|
LicenseContent licenseContent = licenseManager
|
||||||
|
.install(new File(getLicensePath() + "clientLicense/license.lic"));
|
||||||
|
log.info("证书认证通过,安装成功");
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
throw new VerifyException("证书认证失败:" + e + " " + e.getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
//卸载证书
|
||||||
|
public void uninstallLicense() throws Exception {
|
||||||
|
if (licenseManager != null) {
|
||||||
|
licenseManager.uninstall();
|
||||||
|
//Log.info("证书已卸载");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//即时验证证书合法性
|
||||||
|
public void verify() throws Exception {
|
||||||
|
if (licenseManager != null) {
|
||||||
|
licenseManager.verify();
|
||||||
|
//Log.info("证书认证通过");
|
||||||
|
}
|
||||||
|
throw new VerifyException("证书认证失败:licenseManager is null");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取license文件位置
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
private String getLicensePath() {
|
||||||
|
return licensePath;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@@ -0,0 +1,51 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2022-present Charles7c Authors. All Rights Reserved.
|
||||||
|
* <p>
|
||||||
|
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
* <p>
|
||||||
|
* http://www.gnu.org/licenses/lgpl.html
|
||||||
|
* <p>
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package top.continew.license.config;
|
||||||
|
|
||||||
|
import de.schlichtherle.license.*;
|
||||||
|
import org.springframework.context.annotation.Bean;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
import top.continew.license.bean.LicenseInstallerBean;
|
||||||
|
import top.continew.license.manager.CustomLicenseManager;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Desc:
|
||||||
|
* @Author loach
|
||||||
|
* @ClassName top.continew.license.config.LicenseAutoConfiguration
|
||||||
|
* @Date 2025-04-15 15:17
|
||||||
|
*/
|
||||||
|
@Configuration
|
||||||
|
public class LicenseAutoConfiguration {
|
||||||
|
|
||||||
|
private String licensePath;
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public LicenseVerifyProperties licenseVerifyProperties() {
|
||||||
|
return new LicenseVerifyProperties();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public LicenseInstallerBean licenseInstallerBean(LicenseVerifyProperties properties) {
|
||||||
|
return new LicenseInstallerBean(properties);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public LicenseManager licenseManager(LicenseVerifyProperties properties) {
|
||||||
|
return CustomLicenseManager.getInstance(properties);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@@ -0,0 +1,40 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2022-present Charles7c Authors. All Rights Reserved.
|
||||||
|
* <p>
|
||||||
|
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
* <p>
|
||||||
|
* http://www.gnu.org/licenses/lgpl.html
|
||||||
|
* <p>
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package top.continew.license.config;
|
||||||
|
|
||||||
|
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Desc:
|
||||||
|
* @Author loach
|
||||||
|
* @ClassName top.continew.license.config.LicenseYmlConfig
|
||||||
|
* @Date 2025-04-14 14:56
|
||||||
|
*/
|
||||||
|
@ConfigurationProperties(prefix = "license")
|
||||||
|
public class LicenseVerifyProperties {
|
||||||
|
|
||||||
|
private String savePath;
|
||||||
|
|
||||||
|
public String getSavePath() {
|
||||||
|
return savePath;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSavePath(String savePath) {
|
||||||
|
this.savePath = savePath;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@@ -0,0 +1,65 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2022-present Charles7c Authors. All Rights Reserved.
|
||||||
|
* <p>
|
||||||
|
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
* <p>
|
||||||
|
* http://www.gnu.org/licenses/lgpl.html
|
||||||
|
* <p>
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package top.continew.license.dto;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Desc:
|
||||||
|
* @Author loach
|
||||||
|
* @ClassName top.continew.license.dto.ConfigParam
|
||||||
|
* @Date 2025-03-21 14:50
|
||||||
|
*/
|
||||||
|
public class ConfigParam {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 主题
|
||||||
|
*/
|
||||||
|
private String subject;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 公钥别称
|
||||||
|
*/
|
||||||
|
private String publicAlias;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 访问公钥库的密码
|
||||||
|
*/
|
||||||
|
private String storePass;
|
||||||
|
|
||||||
|
public String getSubject() {
|
||||||
|
return subject;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSubject(String subject) {
|
||||||
|
this.subject = subject;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getPublicAlias() {
|
||||||
|
return publicAlias;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPublicAlias(String publicAlias) {
|
||||||
|
this.publicAlias = publicAlias;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getStorePass() {
|
||||||
|
return storePass;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setStorePass(String storePass) {
|
||||||
|
this.storePass = storePass;
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,83 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2022-present Charles7c Authors. All Rights Reserved.
|
||||||
|
* <p>
|
||||||
|
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
* <p>
|
||||||
|
* http://www.gnu.org/licenses/lgpl.html
|
||||||
|
* <p>
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package top.continew.license.dto;
|
||||||
|
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 额外的服务器硬件校验信息对象,这里的属性可根据需求自定义
|
||||||
|
*
|
||||||
|
* @Desc:
|
||||||
|
* @Author loach
|
||||||
|
* @ClassName top.continew.license.dto.LicenseExtraModel
|
||||||
|
* @Date 2025-03-21 14:28
|
||||||
|
*/
|
||||||
|
public class LicenseExtraModel {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 可被允许的IP地址
|
||||||
|
*/
|
||||||
|
private Set<String> ipAddress;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 可被允许的mac地址
|
||||||
|
*/
|
||||||
|
private Set<String> macAddress;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 可被允许的CPU序列号
|
||||||
|
*/
|
||||||
|
private String cpuSerial;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 可被允许的主板序列号
|
||||||
|
*/
|
||||||
|
private String mainBoardSerial;
|
||||||
|
|
||||||
|
public Set<String> getIpAddress() {
|
||||||
|
return ipAddress;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setIpAddress(Set<String> ipAddress) {
|
||||||
|
this.ipAddress = ipAddress;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Set<String> getMacAddress() {
|
||||||
|
return macAddress;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setMacAddress(Set<String> macAddress) {
|
||||||
|
this.macAddress = macAddress;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getCpuSerial() {
|
||||||
|
return cpuSerial;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setCpuSerial(String cpuSerial) {
|
||||||
|
this.cpuSerial = cpuSerial;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getMainBoardSerial() {
|
||||||
|
return mainBoardSerial;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setMainBoardSerial(String mainBoardSerial) {
|
||||||
|
this.mainBoardSerial = mainBoardSerial;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@@ -0,0 +1,32 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2022-present Charles7c Authors. All Rights Reserved.
|
||||||
|
* <p>
|
||||||
|
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
* <p>
|
||||||
|
* http://www.gnu.org/licenses/lgpl.html
|
||||||
|
* <p>
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package top.continew.license.exception;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 自定义校验异常
|
||||||
|
*
|
||||||
|
* @Desc:
|
||||||
|
* @Author loach
|
||||||
|
* @ClassName top.continew.license.exception.VerifyException
|
||||||
|
* @Date 2025-04-11 14:58
|
||||||
|
*/
|
||||||
|
public class VerifyException extends RuntimeException {
|
||||||
|
public VerifyException(String message) {
|
||||||
|
super(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@@ -0,0 +1,71 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2022-present Charles7c Authors. All Rights Reserved.
|
||||||
|
* <p>
|
||||||
|
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
* <p>
|
||||||
|
* http://www.gnu.org/licenses/lgpl.html
|
||||||
|
* <p>
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package top.continew.license.keyStoreParam;
|
||||||
|
|
||||||
|
import de.schlichtherle.license.AbstractKeyStoreParam;
|
||||||
|
|
||||||
|
import java.io.FileInputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Desc:
|
||||||
|
* @Author loach
|
||||||
|
* @ClassName top.continew.license.keyStoreParam.CustomKeyStoreParam
|
||||||
|
* @Date 2025-04-12 14:50
|
||||||
|
*/
|
||||||
|
public class CustomKeyStoreParam extends AbstractKeyStoreParam {
|
||||||
|
/** 密钥路径,可为磁盘路径,也可为项目资源文件里的路径,如果为磁盘路径需重写getStream()方法 */
|
||||||
|
private String storePath;
|
||||||
|
|
||||||
|
/** 公钥或私钥的别名 */
|
||||||
|
private String alias;
|
||||||
|
|
||||||
|
/** 访问公钥/私钥库的密码 */
|
||||||
|
private String storePass;
|
||||||
|
|
||||||
|
/** 公钥/私钥的密码 */
|
||||||
|
private String keyPass;
|
||||||
|
|
||||||
|
public CustomKeyStoreParam(Class clazz, String resource, String alias, String storePass, String keyPass) {
|
||||||
|
super(clazz, resource);
|
||||||
|
this.storePath = resource;
|
||||||
|
this.alias = alias;
|
||||||
|
this.storePass = storePass;
|
||||||
|
this.keyPass = keyPass;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getAlias() {
|
||||||
|
return alias;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getStorePwd() {
|
||||||
|
return storePass;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getKeyPwd() {
|
||||||
|
return keyPass;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public InputStream getStream() throws IOException {
|
||||||
|
return new FileInputStream(storePath);
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,232 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2022-present Charles7c Authors. All Rights Reserved.
|
||||||
|
* <p>
|
||||||
|
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
* <p>
|
||||||
|
* http://www.gnu.org/licenses/lgpl.html
|
||||||
|
* <p>
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package top.continew.license.manager;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
|
import de.schlichtherle.license.*;
|
||||||
|
import de.schlichtherle.xml.GenericCertificate;
|
||||||
|
import net.lingala.zip4j.ZipFile;
|
||||||
|
import net.lingala.zip4j.exception.ZipException;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
import top.continew.license.bean.LicenseInstallerBean;
|
||||||
|
import top.continew.license.config.LicenseVerifyProperties;
|
||||||
|
import top.continew.license.dto.ConfigParam;
|
||||||
|
import top.continew.license.dto.LicenseExtraModel;
|
||||||
|
import top.continew.license.keyStoreParam.CustomKeyStoreParam;
|
||||||
|
import top.continew.license.utils.ServerInfoUtils;
|
||||||
|
|
||||||
|
import java.io.*;
|
||||||
|
import java.util.prefs.Preferences;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 客户端证书管理类(证书验证)
|
||||||
|
*
|
||||||
|
* @Desc:
|
||||||
|
* @Author loach
|
||||||
|
* @ClassName top.continew.license.manager.ClientLicenseManager
|
||||||
|
* @Date 2025-04-11 15:00
|
||||||
|
*/
|
||||||
|
@Component
|
||||||
|
public class CustomLicenseManager extends LicenseManager {
|
||||||
|
|
||||||
|
private static final Logger log = LoggerFactory.getLogger(CustomLicenseManager.class);
|
||||||
|
|
||||||
|
private static volatile CustomLicenseManager INSTANCE;
|
||||||
|
private LicenseExtraModel extraModel;
|
||||||
|
|
||||||
|
public static CustomLicenseManager getInstance(LicenseVerifyProperties properties) {
|
||||||
|
if (INSTANCE == null) {
|
||||||
|
synchronized (CustomLicenseManager.class) {
|
||||||
|
if (INSTANCE == null) {
|
||||||
|
INSTANCE = new CustomLicenseManager(properties);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return INSTANCE;
|
||||||
|
}
|
||||||
|
|
||||||
|
private String licensePath;
|
||||||
|
|
||||||
|
public CustomLicenseManager(LicenseVerifyProperties properties) {
|
||||||
|
if (properties == null || properties.getSavePath() == null) {
|
||||||
|
String os = System.getProperty("os.name");
|
||||||
|
if (os.toLowerCase().contains("windows")) {
|
||||||
|
this.licensePath = "D:/license/";
|
||||||
|
}
|
||||||
|
this.licensePath = "/data/license/";
|
||||||
|
} else {
|
||||||
|
this.licensePath = properties.getSavePath();
|
||||||
|
|
||||||
|
}
|
||||||
|
//初始化服务信息
|
||||||
|
initServerExtraModel();
|
||||||
|
//解压证书和配置文件等
|
||||||
|
extractZip();
|
||||||
|
//获取配置文件
|
||||||
|
ConfigParam configParam = getConfigParam();
|
||||||
|
//安装证书
|
||||||
|
Preferences preferences = Preferences.userNodeForPackage(LicenseInstallerBean.class);
|
||||||
|
CipherParam cipherParam = new DefaultCipherParam(configParam.getStorePass());
|
||||||
|
KeyStoreParam publicKeyStoreParam = new CustomKeyStoreParam(LicenseInstallerBean.class, getLicensePath() + "clientLicense/publicCerts.keystore", configParam
|
||||||
|
.getPublicAlias(), configParam.getStorePass(), null);
|
||||||
|
LicenseParam licenseParam = new DefaultLicenseParam(configParam
|
||||||
|
.getSubject(), preferences, publicKeyStoreParam, cipherParam);
|
||||||
|
|
||||||
|
super.setLicenseParam(licenseParam);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void initServerExtraModel() {
|
||||||
|
this.extraModel = ServerInfoUtils.getServerInfos();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 解压压缩包
|
||||||
|
*
|
||||||
|
* @throws ZipException
|
||||||
|
*/
|
||||||
|
private void extractZip() {
|
||||||
|
ZipFile config = new ZipFile(getLicensePath() + "clientLicense.zip");
|
||||||
|
File licenseDir = new File(getLicensePath() + "clientLicense");
|
||||||
|
if (!licenseDir.exists()) {
|
||||||
|
licenseDir.mkdir();
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
config.extractAll(licenseDir.getAbsolutePath());
|
||||||
|
} catch (ZipException e) {
|
||||||
|
log.error(e.getMessage());
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取压缩文件中配置的基础参数
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
* @throws Exception
|
||||||
|
*/
|
||||||
|
private ConfigParam getConfigParam() {
|
||||||
|
FileInputStream config = null;
|
||||||
|
BufferedReader reader = null;
|
||||||
|
try {
|
||||||
|
config = new FileInputStream(getLicensePath() + "clientLicense/clientConfig.json");
|
||||||
|
reader = new BufferedReader(new InputStreamReader(config, "UTF-8"));
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
String temp = null;
|
||||||
|
while ((temp = reader.readLine()) != null) {
|
||||||
|
sb.append(temp);
|
||||||
|
}
|
||||||
|
ObjectMapper mapper = new ObjectMapper();
|
||||||
|
ConfigParam configParam = mapper.readValue(sb.toString(), ConfigParam.class);
|
||||||
|
return configParam;
|
||||||
|
} catch (IOException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
} finally {
|
||||||
|
if (reader != null) {
|
||||||
|
try {
|
||||||
|
reader.close();
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (config != null) {
|
||||||
|
try {
|
||||||
|
config.close();
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取license文件位置
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
private String getLicensePath() {
|
||||||
|
return this.licensePath;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 重写验证证书方法,添加自定义参数验证
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
protected synchronized void validate(LicenseContent content) throws LicenseContentException {
|
||||||
|
//系统验证基本参数:生效时间、失效时间、公钥别名、公钥密码
|
||||||
|
super.validate(content);
|
||||||
|
//验证自定义参数
|
||||||
|
Object o = content.getExtra();
|
||||||
|
if (o != null && extraModel != null && o instanceof LicenseExtraModel) {
|
||||||
|
LicenseExtraModel contentExtraModel = (LicenseExtraModel)o;
|
||||||
|
if (!contentExtraModel.getCpuSerial().equals(extraModel.getCpuSerial())) {
|
||||||
|
throw new LicenseContentException("CPU核数不匹配");
|
||||||
|
}
|
||||||
|
if (!contentExtraModel.getMainBoardSerial().equals(extraModel.getMainBoardSerial())) {
|
||||||
|
throw new LicenseContentException("主板序列号不匹配");
|
||||||
|
}
|
||||||
|
if (!contentExtraModel.getIpAddress().equals(extraModel.getIpAddress())) {
|
||||||
|
throw new LicenseContentException("IP地址不匹配");
|
||||||
|
}
|
||||||
|
if (!contentExtraModel.getMacAddress().equals(extraModel.getMacAddress())) {
|
||||||
|
throw new LicenseContentException("MAC地址不匹配");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
throw new LicenseContentException("证书无效");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 重写证书安装方法,主要是更改调用本类的验证方法
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
protected synchronized LicenseContent install(final byte[] key, LicenseNotary notary) throws Exception {
|
||||||
|
|
||||||
|
final GenericCertificate certificate = getPrivacyGuard().key2cert(key);
|
||||||
|
notary.verify(certificate);
|
||||||
|
final LicenseContent content = (LicenseContent)certificate.getContent();
|
||||||
|
this.validate(content);
|
||||||
|
setLicenseKey(key);
|
||||||
|
setCertificate(certificate);
|
||||||
|
|
||||||
|
return content;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 重写验证证书合法的方法,主要是更改调用本类的验证方法
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
protected synchronized LicenseContent verify(LicenseNotary notary) throws Exception {
|
||||||
|
GenericCertificate certificate = getCertificate();
|
||||||
|
if (null != certificate)
|
||||||
|
return (LicenseContent)certificate.getContent();
|
||||||
|
|
||||||
|
// Load license key from preferences,
|
||||||
|
final byte[] key = getLicenseKey();
|
||||||
|
if (null == key)
|
||||||
|
throw new NoLicenseInstalledException(getLicenseParam().getSubject());
|
||||||
|
certificate = getPrivacyGuard().key2cert(key);
|
||||||
|
notary.verify(certificate);
|
||||||
|
final LicenseContent content = (LicenseContent)certificate.getContent();
|
||||||
|
this.validate(content);
|
||||||
|
setCertificate(certificate);
|
||||||
|
|
||||||
|
return content;
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,338 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2022-present Charles7c Authors. All Rights Reserved.
|
||||||
|
* <p>
|
||||||
|
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
* <p>
|
||||||
|
* http://www.gnu.org/licenses/lgpl.html
|
||||||
|
* <p>
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package top.continew.license.utils;
|
||||||
|
|
||||||
|
import cn.hutool.core.collection.CollUtil;
|
||||||
|
import cn.hutool.core.collection.CollectionUtil;
|
||||||
|
import cn.hutool.core.io.FileUtil;
|
||||||
|
import cn.hutool.core.io.IoUtil;
|
||||||
|
import cn.hutool.core.util.StrUtil;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
import top.continew.license.dto.LicenseExtraModel;
|
||||||
|
import top.continew.license.exception.VerifyException;
|
||||||
|
|
||||||
|
import java.io.*;
|
||||||
|
import java.net.InetAddress;
|
||||||
|
import java.net.NetworkInterface;
|
||||||
|
import java.net.SocketException;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.Enumeration;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 服务器信息工具类
|
||||||
|
*
|
||||||
|
* @author Rong.Jia
|
||||||
|
* @date 2022/03/10
|
||||||
|
*/
|
||||||
|
public class ServerInfoUtils {
|
||||||
|
|
||||||
|
private static final Logger log = LoggerFactory.getLogger(ServerInfoUtils.class);
|
||||||
|
|
||||||
|
private static class ServerInfosContainer {
|
||||||
|
private static Set<String> ipAddress = null;
|
||||||
|
private static Set<String> macAddress = null;
|
||||||
|
private static String cpuSerial = null;
|
||||||
|
private static String mainBoardSerial = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 组装需要额外校验的License参数
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public static LicenseExtraModel getServerInfos() {
|
||||||
|
LicenseExtraModel result = new LicenseExtraModel();
|
||||||
|
try {
|
||||||
|
initServerInfos();
|
||||||
|
result.setIpAddress(ServerInfosContainer.ipAddress);
|
||||||
|
result.setMacAddress(ServerInfosContainer.macAddress);
|
||||||
|
result.setCpuSerial(ServerInfosContainer.cpuSerial);
|
||||||
|
result.setMainBoardSerial(ServerInfosContainer.mainBoardSerial);
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("获取服务器硬件信息异常", e);
|
||||||
|
throw new VerifyException(String.format("获取服务器硬件信息异常, %s", e.getMessage()));
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 初始化服务器硬件信息,并将信息缓存到内存
|
||||||
|
*
|
||||||
|
* @throws Exception 默认异常
|
||||||
|
*/
|
||||||
|
private static void initServerInfos() throws Exception {
|
||||||
|
if (ServerInfosContainer.ipAddress == null) {
|
||||||
|
ServerInfosContainer.ipAddress = getIpAddress();
|
||||||
|
}
|
||||||
|
if (ServerInfosContainer.macAddress == null) {
|
||||||
|
ServerInfosContainer.macAddress = getMacAddress();
|
||||||
|
}
|
||||||
|
if (ServerInfosContainer.cpuSerial == null) {
|
||||||
|
ServerInfosContainer.cpuSerial = getCpuSerial();
|
||||||
|
}
|
||||||
|
if (ServerInfosContainer.mainBoardSerial == null) {
|
||||||
|
ServerInfosContainer.mainBoardSerial = getMainBoardSerial();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取服务器临时磁盘位置
|
||||||
|
*
|
||||||
|
* @return {@link String}
|
||||||
|
*/
|
||||||
|
public static String getServerTempPath() {
|
||||||
|
return System.getProperty("user.dir");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取CPU序列号
|
||||||
|
*
|
||||||
|
* @return String 主板序列号
|
||||||
|
*/
|
||||||
|
public static String getCpuSerial() {
|
||||||
|
return FileUtil.isWindows() ? getWindowCpuSerial() : getLinuxCpuSerial();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取主板序列号
|
||||||
|
*
|
||||||
|
* @return String 主板序列号
|
||||||
|
*/
|
||||||
|
public static String getMainBoardSerial() {
|
||||||
|
return FileUtil.isWindows() ? getWindowMainBoardSerial() : getLinuxMainBoardSerial();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取linux cpu 序列号
|
||||||
|
*
|
||||||
|
* @return {@link String}
|
||||||
|
*/
|
||||||
|
private static String getLinuxCpuSerial() {
|
||||||
|
String result = StrUtil.EMPTY;
|
||||||
|
String cpuIdCmd = "dmidecode";
|
||||||
|
BufferedReader bufferedReader = null;
|
||||||
|
try {
|
||||||
|
// 管道
|
||||||
|
Process p = Runtime.getRuntime().exec(new String[] {"sh", "-c", cpuIdCmd});
|
||||||
|
bufferedReader = new BufferedReader(new InputStreamReader(p.getInputStream()));
|
||||||
|
String line = null;
|
||||||
|
int index = -1;
|
||||||
|
while ((line = bufferedReader.readLine()) != null) {
|
||||||
|
// 寻找标示字符串[hwaddr]
|
||||||
|
index = line.toLowerCase().indexOf("uuid");
|
||||||
|
if (index >= 0) {
|
||||||
|
// 取出mac地址并去除2边空格
|
||||||
|
result = line.substring(index + "uuid".length() + 1).trim();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (IOException e) {
|
||||||
|
log.error("获取Linux cpu信息错误 {}", e.getMessage());
|
||||||
|
} finally {
|
||||||
|
IoUtil.close(bufferedReader);
|
||||||
|
}
|
||||||
|
return result.trim();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取Window cpu 序列号
|
||||||
|
*
|
||||||
|
* @return {@link String}
|
||||||
|
*/
|
||||||
|
private static String getWindowCpuSerial() {
|
||||||
|
|
||||||
|
String result = StrUtil.EMPTY;
|
||||||
|
File file = null;
|
||||||
|
BufferedReader input = null;
|
||||||
|
try {
|
||||||
|
file = File.createTempFile("tmp", ".vbs");
|
||||||
|
file.deleteOnExit();
|
||||||
|
FileWriter fw = new FileWriter(file);
|
||||||
|
String vbs = "Set objWMIService = GetObject(\"winmgmts:\\\\.\\root\\cimv2\")\n" + "Set colItems = objWMIService.ExecQuery _ \n" + " (\"Select * from Win32_Processor\") \n" + "For Each objItem in colItems \n" + " Wscript.Echo objItem.ProcessorId \n" + " exit for ' do the first cpu only! \n" + "Next \n";
|
||||||
|
|
||||||
|
fw.write(vbs);
|
||||||
|
fw.close();
|
||||||
|
|
||||||
|
Process p = Runtime.getRuntime().exec("cscript //NoLogo " + file.getPath());
|
||||||
|
input = new BufferedReader(new InputStreamReader(p.getInputStream()));
|
||||||
|
String line;
|
||||||
|
while ((line = input.readLine()) != null) {
|
||||||
|
result += line;
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("获取window cpu信息错误, {}", e.getMessage());
|
||||||
|
} finally {
|
||||||
|
IoUtil.close(input);
|
||||||
|
FileUtil.del(file);
|
||||||
|
}
|
||||||
|
return result.trim();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取Linux主板序列号
|
||||||
|
*
|
||||||
|
* @return {@link String}
|
||||||
|
*/
|
||||||
|
private static String getLinuxMainBoardSerial() {
|
||||||
|
String result = StrUtil.EMPTY;
|
||||||
|
String maniBordCmd = "dmidecode | grep 'Serial Number' | awk '{print $3}' | tail -1";
|
||||||
|
BufferedReader br = null;
|
||||||
|
try {
|
||||||
|
Process p = Runtime.getRuntime().exec(new String[] {"sh", "-c", maniBordCmd});
|
||||||
|
br = new BufferedReader(new InputStreamReader(p.getInputStream()));
|
||||||
|
String line;
|
||||||
|
while ((line = br.readLine()) != null) {
|
||||||
|
result += line;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} catch (IOException e) {
|
||||||
|
log.error("获取Linux主板信息错误 {}", e.getMessage());
|
||||||
|
} finally {
|
||||||
|
IoUtil.close(br);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取window主板序列号
|
||||||
|
*
|
||||||
|
* @return {@link String}
|
||||||
|
*/
|
||||||
|
private static String getWindowMainBoardSerial() {
|
||||||
|
String result = StrUtil.EMPTY;
|
||||||
|
File file = null;
|
||||||
|
BufferedReader input = null;
|
||||||
|
try {
|
||||||
|
file = File.createTempFile("realhowto", ".vbs");
|
||||||
|
file.deleteOnExit();
|
||||||
|
FileWriter fw = new FileWriter(file);
|
||||||
|
|
||||||
|
String vbs = "Set objWMIService = GetObject(\"winmgmts:\\\\.\\root\\cimv2\")\n" + "Set colItems = objWMIService.ExecQuery _ \n" + " (\"Select * from Win32_BaseBoard\") \n" + "For Each objItem in colItems \n" + " Wscript.Echo objItem.SerialNumber \n" + " exit for ' do the first cpu only! \n" + "Next \n";
|
||||||
|
|
||||||
|
fw.write(vbs);
|
||||||
|
fw.close();
|
||||||
|
Process p = Runtime.getRuntime().exec("cscript //NoLogo " + file.getPath());
|
||||||
|
input = new BufferedReader(new InputStreamReader(p.getInputStream()));
|
||||||
|
String line;
|
||||||
|
while ((line = input.readLine()) != null) {
|
||||||
|
result += line;
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("获取Window主板信息错误 {}", e.getMessage());
|
||||||
|
} finally {
|
||||||
|
IoUtil.close(input);
|
||||||
|
FileUtil.del(file);
|
||||||
|
}
|
||||||
|
return result.trim();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>获取Mac地址</p>
|
||||||
|
*
|
||||||
|
* @return List<String> Mac地址
|
||||||
|
* @throws Exception 默认异常
|
||||||
|
*/
|
||||||
|
public static Set<String> getMacAddress() throws Exception {
|
||||||
|
// 获取所有网络接口
|
||||||
|
Set<InetAddress> inetAddresses = getLocalAllInetAddress();
|
||||||
|
if (CollectionUtil.isNotEmpty(inetAddresses)) {
|
||||||
|
return inetAddresses.stream()
|
||||||
|
.map(ServerInfoUtils::getMacByInetAddress)
|
||||||
|
.distinct()
|
||||||
|
.collect(Collectors.toSet());
|
||||||
|
}
|
||||||
|
return Collections.emptySet();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>获取IP地址</p>
|
||||||
|
*
|
||||||
|
* @return List<String> IP地址
|
||||||
|
* @throws Exception 默认异常
|
||||||
|
*/
|
||||||
|
public static Set<String> getIpAddress() throws Exception {
|
||||||
|
// 获取所有网络接口
|
||||||
|
Set<InetAddress> inetAddresses = getLocalAllInetAddress();
|
||||||
|
if (CollectionUtil.isNotEmpty(inetAddresses)) {
|
||||||
|
return inetAddresses.stream()
|
||||||
|
.map(InetAddress::getHostAddress)
|
||||||
|
.distinct()
|
||||||
|
.map(String::toLowerCase)
|
||||||
|
.collect(Collectors.toSet());
|
||||||
|
}
|
||||||
|
return Collections.emptySet();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>获取某个网络地址对应的Mac地址</p>
|
||||||
|
*
|
||||||
|
* @param inetAddr 网络地址
|
||||||
|
* @return String Mac地址
|
||||||
|
*/
|
||||||
|
private static String getMacByInetAddress(InetAddress inetAddr) {
|
||||||
|
try {
|
||||||
|
byte[] mac = NetworkInterface.getByInetAddress(inetAddr).getHardwareAddress();
|
||||||
|
StringBuilder stringBuilder = new StringBuilder();
|
||||||
|
for (int i = 0; i < mac.length; i++) {
|
||||||
|
if (i != 0) {
|
||||||
|
stringBuilder.append("-");
|
||||||
|
}
|
||||||
|
// 将十六进制byte转化为字符串
|
||||||
|
String temp = Integer.toHexString(mac[i] & 0xff);
|
||||||
|
if (temp.length() == 1) {
|
||||||
|
stringBuilder.append("0").append(temp);
|
||||||
|
} else {
|
||||||
|
stringBuilder.append(temp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return stringBuilder.toString().toUpperCase();
|
||||||
|
} catch (SocketException e) {
|
||||||
|
log.error("getMacByInetAddress {}", e.getMessage());
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>获取当前服务器所有符合条件的网络地址</p>
|
||||||
|
*
|
||||||
|
* @return List<InetAddress> 网络地址列表
|
||||||
|
* @throws Exception 默认异常
|
||||||
|
*/
|
||||||
|
private static Set<InetAddress> getLocalAllInetAddress() throws Exception {
|
||||||
|
|
||||||
|
Set<InetAddress> result = CollUtil.newHashSet();
|
||||||
|
// 遍历所有的网络接口
|
||||||
|
for (Enumeration<NetworkInterface> networkInterfaces = NetworkInterface.getNetworkInterfaces();
|
||||||
|
networkInterfaces.hasMoreElements();) {
|
||||||
|
NetworkInterface ni = networkInterfaces.nextElement();
|
||||||
|
// 在所有的接口下再遍历IP
|
||||||
|
for (Enumeration<InetAddress> addresses = ni.getInetAddresses(); addresses.hasMoreElements();) {
|
||||||
|
InetAddress address = addresses.nextElement();
|
||||||
|
//排除LoopbackAddress、SiteLocalAddress、LinkLocalAddress、MulticastAddress类型的IP地址
|
||||||
|
/*&& !inetAddr.isSiteLocalAddress()*/
|
||||||
|
if (!address.isLoopbackAddress() && !address.isLinkLocalAddress() && !address.isMulticastAddress()) {
|
||||||
|
result.add(address);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@@ -0,0 +1,2 @@
|
|||||||
|
top.continew.license.config.LicenseAutoConfiguration
|
||||||
|
top.continew.license.Initializing.LicenseStarterInitializingBean
|
@@ -0,0 +1,2 @@
|
|||||||
|
license:
|
||||||
|
savePath: C:/license/
|
21
continew-starter-license/pom.xml
Normal file
21
continew-starter-license/pom.xml
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||||
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
<parent>
|
||||||
|
<groupId>top.continew</groupId>
|
||||||
|
<artifactId>continew-starter</artifactId>
|
||||||
|
<version>${revision}</version>
|
||||||
|
</parent>
|
||||||
|
|
||||||
|
<artifactId>continew-starter-license</artifactId>
|
||||||
|
<packaging>pom</packaging>
|
||||||
|
<description>ContiNew Starter license管理模块</description>
|
||||||
|
<modules>
|
||||||
|
<module>continew-starter-license-generate</module>
|
||||||
|
<module>continew-starter-license-verify</module>
|
||||||
|
</modules>
|
||||||
|
|
||||||
|
|
||||||
|
</project>
|
1
pom.xml
1
pom.xml
@@ -51,6 +51,7 @@
|
|||||||
<module>continew-starter-auth</module>
|
<module>continew-starter-auth</module>
|
||||||
<module>continew-starter-messaging</module>
|
<module>continew-starter-messaging</module>
|
||||||
<module>continew-starter-extension</module>
|
<module>continew-starter-extension</module>
|
||||||
|
<module>continew-starter-license</module>
|
||||||
</modules>
|
</modules>
|
||||||
|
|
||||||
<build>
|
<build>
|
||||||
|
Reference in New Issue
Block a user