feat(license): 增加核心模块,优化代码结构

This commit is contained in:
liquor
2025-04-17 11:58:52 +00:00
committed by Charles7c
parent 1ce5c023cf
commit 06f5a0f346
29 changed files with 515 additions and 954 deletions

View File

@@ -276,12 +276,21 @@
<artifactId>continew-starter-core</artifactId> <artifactId>continew-starter-core</artifactId>
<version>${revision}</version> <version>${revision}</version>
</dependency> </dependency>
<!-- license 核心模块 -->
<dependency>
<groupId>top.continew</groupId>
<artifactId>continew-starter-license-core</artifactId>
<version>${revision}</version>
</dependency>
<!-- license 生成模块 --> <!-- license 生成模块 -->
<dependency> <dependency>
<groupId>top.continew</groupId> <groupId>top.continew</groupId>
<artifactId>continew-starter-license-generate</artifactId> <artifactId>continew-starter-license-generate</artifactId>
<version>${revision}</version> <version>${revision}</version>
</dependency> </dependency>
<!-- license 校验模块 --> <!-- license 校验模块 -->
<dependency> <dependency>
<groupId>top.continew</groupId> <groupId>top.continew</groupId>

View File

@@ -0,0 +1,33 @@
<?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 https://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-core</artifactId>
<description>ContiNew Starter License核心模块</description>
<dependencies>
<!-- continew starter 核心模块 -->
<dependency>
<groupId>top.continew</groupId>
<artifactId>continew-starter-core</artifactId>
</dependency>
<!-- license 依赖-->
<dependency>
<groupId>de.schlichtherle.truelicense</groupId>
<artifactId>truelicense-core</artifactId>
</dependency>
<!--zip4j压缩文件-->
<dependency>
<groupId>net.lingala.zip4j</groupId>
<artifactId>zip4j</artifactId>
</dependency>
</dependencies>
</project>

View File

@@ -16,17 +16,34 @@
package top.continew.license.exception; package top.continew.license.exception;
import top.continew.starter.core.exception.BaseException;
import java.io.Serial;
/** /**
* 自定义证书认证异常 * 自定义证书认证异常
* *
* @Desc: * @author loach
* @Author loach * @author echo
* @ClassName top.continew.license.exception.LicenseException * @since 2.11.0
* @Date 2025-03-21 14:30
*/ */
public class LicenseException extends RuntimeException { public class LicenseException extends BaseException {
@Serial
private static final long serialVersionUID = 1L;
public LicenseException() {
}
public LicenseException(String message) { public LicenseException(String message) {
super(message); super(message);
} }
public LicenseException(Throwable cause) {
super(cause);
}
public LicenseException(String message, Throwable cause) {
super(message, cause);
}
} }

View File

@@ -0,0 +1,68 @@
/*
* 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.model;
import net.lingala.zip4j.ZipFile;
import java.io.Serial;
import java.io.Serializable;
/**
* 生成创建者返回参数
*
* @author echo
* @since 2.11.0
**/
public class BuildCreatorResp implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
/**
* 许可证创建者参数
*/
private LicenseCreatorParam param;
/**
* 客户端zip文件
*/
private ZipFile clientZipFile;
public BuildCreatorResp(LicenseCreatorParam param, ZipFile clientZipFile) {
this.param = param;
this.clientZipFile = clientZipFile;
}
public BuildCreatorResp() {
}
public LicenseCreatorParam getParam() {
return param;
}
public void setParam(LicenseCreatorParam param) {
this.param = param;
}
public ZipFile getClientZipFile() {
return clientZipFile;
}
public void setClientZipFile(ZipFile clientZipFile) {
this.clientZipFile = clientZipFile;
}
}

View File

@@ -14,14 +14,14 @@
* limitations under the License. * limitations under the License.
*/ */
package top.continew.license.dto; package top.continew.license.model;
/** /**
* @Desc: * config参数
* @Author loach *
* @ClassName top.continew.license.dto.ConfigParam * @author loach
* @Date 2025-03-21 14:50 * @since 2.11.0
*/ **/
public class ConfigParam { public class ConfigParam {
/** /**

View File

@@ -14,7 +14,7 @@
* limitations under the License. * limitations under the License.
*/ */
package top.continew.license.keyStoreParam; package top.continew.license.model;
import de.schlichtherle.license.AbstractKeyStoreParam; import de.schlichtherle.license.AbstractKeyStoreParam;
@@ -23,24 +23,37 @@ import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
/** /**
* @Desc: * 自定义密钥存储参数
* @Author loach *
* @ClassName top.continew.license.keyStoreParam.CustomKeyStoreParam * @author loach
* @Date 2025-04-12 14:50 * @author echo
* @since 2.11.0
*/ */
public class CustomKeyStoreParam extends AbstractKeyStoreParam { public class CustomKeyStoreParam extends AbstractKeyStoreParam {
/** 密钥路径,可为磁盘路径,也可为项目资源文件里的路径,如果为磁盘路径需重写getStream()方法 */ /**
* 密钥路径可为磁盘路径也可为项目资源文件里的路径,如果为磁盘路径需重写getStream()方法
*/
private String storePath; private String storePath;
/** 公钥或私钥的别名 */ /**
* 公钥或私钥的别名
*/
private String alias; private String alias;
/** 访问公钥/私钥库的密码 */ /**
* 访问公钥/私钥库的密码
*/
private String storePass; private String storePass;
/** 公钥/私钥的密码 */ /**
* 公钥/私钥的密码
*/
private String keyPass; private String keyPass;
public CustomKeyStoreParam(Class aClass, String s) {
super(aClass, s);
}
public CustomKeyStoreParam(Class clazz, String resource, String alias, String storePass, String keyPass) { public CustomKeyStoreParam(Class clazz, String resource, String alias, String storePass, String keyPass) {
super(clazz, resource); super(clazz, resource);
this.storePath = resource; this.storePath = resource;

View File

@@ -14,42 +14,56 @@
* limitations under the License. * limitations under the License.
*/ */
package top.continew.license.dto; package top.continew.license.model;
import java.io.Serial; import java.io.Serial;
import java.io.Serializable; import java.io.Serializable;
import java.util.Date; import java.util.Date;
/** /**
* @Desc: * 许可证创建者参数
* @Author loach *
* @ClassName top.continew.license.dto.LicenseCreatorParam * @author loach
* @Date 2025-03-21 14:22 * @since 2.11.0
*/ **/
public class LicenseCreatorParam implements Serializable { public class LicenseCreatorParam implements Serializable {
@Serial @Serial
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
/** 证书主题 */ /**
* 证书主题
*/
private String subject; private String subject;
/** 私钥别称 */ /**
* 私钥别称
*/
private String privateAlias; private String privateAlias;
/** 私钥密码 */ /**
* 私钥密码
*/
private String keyPass; private String keyPass;
/** 访问公钥库的密码 */ /**
* 访问公钥库的密码
*/
private String storePass; private String storePass;
/** 证书生成路径 */ /**
* 证书生成路径
*/
private String licensePath; private String licensePath;
/** 私钥库存储路径 */ /**
* 私钥库存储路径
*/
private String privateKeysStorePath; private String privateKeysStorePath;
/** 证书生效时间 */ /**
* 证书生效时间
*/
private Date issuedTime = new Date(); private Date issuedTime = new Date();
/** /**
@@ -67,10 +81,14 @@ public class LicenseCreatorParam implements Serializable {
*/ */
private Integer consumerAmount = 1; private Integer consumerAmount = 1;
/** 描述信息 */ /**
* 描述信息
*/
private String description; private String description;
/** 额外的服务器硬件校验信息 */ /**
* 额外的服务器硬件校验信息
*/
private LicenseExtraModel licenseExtraModel; private LicenseExtraModel licenseExtraModel;
public String getSubject() { public String getSubject() {

View File

@@ -14,18 +14,16 @@
* limitations under the License. * limitations under the License.
*/ */
package top.continew.license.dto; package top.continew.license.model;
import java.util.Date; import java.util.Date;
/** /**
* 为用户生成证书需要的具体参数 * 为用户生成证书需要的具体参数
* *
* @Desc: * @author loach
* @Author loach * @since 2.11.0
* @ClassName top.continew.license.dto.LicenseCreatorParamVO **/
* @Date 2025-03-21 14:29
*/
public class LicenseCreatorParamVO { public class LicenseCreatorParamVO {
/** /**
@@ -58,14 +56,6 @@ public class LicenseCreatorParamVO {
*/ */
private String licenseSavePath; private String licenseSavePath;
public String getLicenseSavePath() {
return licenseSavePath;
}
public void setLicenseSavePath(String licenseSavePath) {
this.licenseSavePath = licenseSavePath;
}
/** /**
* 额外的服务器硬件校验信息 * 额外的服务器硬件校验信息
*/ */
@@ -111,6 +101,14 @@ public class LicenseCreatorParamVO {
this.description = description; this.description = description;
} }
public String getLicenseSavePath() {
return licenseSavePath;
}
public void setLicenseSavePath(String licenseSavePath) {
this.licenseSavePath = licenseSavePath;
}
public LicenseExtraModel getLicenseExtraModel() { public LicenseExtraModel getLicenseExtraModel() {
return licenseExtraModel; return licenseExtraModel;
} }

View File

@@ -14,18 +14,16 @@
* limitations under the License. * limitations under the License.
*/ */
package top.continew.license.dto; package top.continew.license.model;
import java.util.Set; import java.util.Set;
/** /**
* 额外的服务器硬件校验信息对象,这里的属性可根据需求自定义 * 额外的服务器硬件校验信息对象,这里的属性可根据需求自定义
* *
* @Desc: * @author loach
* @Author loach * @since 2.11.0
* @ClassName top.continew.license.dto.LicenseExtraModel **/
* @Date 2025-03-21 14:28
*/
public class LicenseExtraModel { public class LicenseExtraModel {
/** /**

View File

@@ -16,21 +16,19 @@
package top.continew.license.util; package top.continew.license.util;
import cn.hutool.core.util.ArrayUtil;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.io.InputStreamReader; import java.io.InputStreamReader;
import java.io.LineNumberReader; import java.io.LineNumberReader;
import cn.hutool.core.util.ArrayUtil;
/** /**
* 运行命令行工具类 * 运行命令行工具类
* *
* @Desc: * @author loach
* @Author loach * @since 2.11.0
* @ClassName top.continew.license.util.ExecCmdUtil **/
* @Date 2025-03-22 18:44
*/
public class ExecCmdUtil { 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"; private static final String CREATE_3RDSESSION_SHELL_SCRIPT = "head -n 80 /dev/urandom | tr -dc A-Za-z0-9 | head -c 168";
@@ -42,8 +40,8 @@ public class ExecCmdUtil {
* @return String 返回打印信息 * @return String 返回打印信息
*/ */
public static String exec(String... cmd) throws IOException { public static String exec(String... cmd) throws IOException {
Process process = null; Process process;
if (System.getProperty("os.name").indexOf("Windows") != -1) { if (System.getProperty("os.name").contains("Windows")) {
if (cmd != null && cmd.length == 1) { if (cmd != null && cmd.length == 1) {
process = Runtime.getRuntime().exec(cmd[0]); process = Runtime.getRuntime().exec(cmd[0]);
} else { } else {
@@ -59,6 +57,12 @@ public class ExecCmdUtil {
return print + " " + err; return print + " " + err;
} }
/**
* 读取 InputStream 内容为字符串使用 GBK 编码
*
* @param in 输入流
* @return 拼接后的字符串读取失败返回空字符串
*/
private static String readProcess(InputStream in) { private static String readProcess(InputStream in) {
try (LineNumberReader print = new LineNumberReader(new InputStreamReader(in, "GBK"))) { try (LineNumberReader print = new LineNumberReader(new InputStreamReader(in, "GBK"))) {
StringBuffer sb = new StringBuffer(); StringBuffer sb = new StringBuffer();

View File

@@ -23,8 +23,8 @@ import cn.hutool.core.io.IoUtil;
import cn.hutool.core.util.StrUtil; import cn.hutool.core.util.StrUtil;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import top.continew.license.dto.LicenseExtraModel;
import top.continew.license.exception.LicenseException; import top.continew.license.exception.LicenseException;
import top.continew.license.model.LicenseExtraModel;
import java.io.*; import java.io.*;
import java.net.InetAddress; import java.net.InetAddress;
@@ -39,7 +39,7 @@ import java.util.stream.Collectors;
* 服务器信息工具类 * 服务器信息工具类
* *
* @author Rong.Jia * @author Rong.Jia
* @date 2022/03/10 * @since 2.11.0
*/ */
public class ServerInfoUtils { public class ServerInfoUtils {
@@ -55,7 +55,7 @@ public class ServerInfoUtils {
/** /**
* 组装需要额外校验的License参数 * 组装需要额外校验的License参数
* *
* @return * @return {@link LicenseExtraModel }
*/ */
public static LicenseExtraModel getServerInfos() { public static LicenseExtraModel getServerInfos() {
LicenseExtraModel result = new LicenseExtraModel(); LicenseExtraModel result = new LicenseExtraModel();
@@ -75,7 +75,7 @@ public class ServerInfoUtils {
/** /**
* 初始化服务器硬件信息并将信息缓存到内存 * 初始化服务器硬件信息并将信息缓存到内存
* *
* @throws Exception 默认异常 * @throws Exception 例外
*/ */
private static void initServerInfos() throws Exception { private static void initServerInfos() throws Exception {
if (ServerInfosContainer.ipAddress == null) { if (ServerInfosContainer.ipAddress == null) {
@@ -104,7 +104,7 @@ public class ServerInfoUtils {
/** /**
* 获取CPU序列号 * 获取CPU序列号
* *
* @return String 主板序列号 * @return String CPU 序列号
*/ */
public static String getCpuSerial() { public static String getCpuSerial() {
return FileUtil.isWindows() ? getWindowCpuSerial() : getLinuxCpuSerial(); return FileUtil.isWindows() ? getWindowCpuSerial() : getLinuxCpuSerial();
@@ -158,15 +158,22 @@ public class ServerInfoUtils {
*/ */
private static String getWindowCpuSerial() { private static String getWindowCpuSerial() {
String result = StrUtil.EMPTY; StringBuilder result = new StringBuilder(StrUtil.EMPTY);
File file = null; File file = null;
BufferedReader input = null; BufferedReader input = null;
try { try {
file = File.createTempFile("tmp", ".vbs"); file = File.createTempFile("tmp", ".vbs");
file.deleteOnExit(); file.deleteOnExit();
FileWriter fw = new FileWriter(file); 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"; String vbs = """
Set objWMIService = GetObject("winmgmts:\\\\.\\root\\cimv2")
Set colItems = objWMIService.ExecQuery("Select * from Win32_Processor")
For Each objItem In colItems
WScript.Echo objItem.ProcessorId
Exit For ' do the first cpu only!
Next
""";
fw.write(vbs); fw.write(vbs);
fw.close(); fw.close();
@@ -174,7 +181,7 @@ public class ServerInfoUtils {
input = new BufferedReader(new InputStreamReader(p.getInputStream())); input = new BufferedReader(new InputStreamReader(p.getInputStream()));
String line; String line;
while ((line = input.readLine()) != null) { while ((line = input.readLine()) != null) {
result += line; result.append(line);
} }
} catch (Exception e) { } catch (Exception e) {
log.error("获取window cpu信息错误, {}", e.getMessage()); log.error("获取window cpu信息错误, {}", e.getMessage());
@@ -182,7 +189,7 @@ public class ServerInfoUtils {
IoUtil.close(input); IoUtil.close(input);
FileUtil.del(file); FileUtil.del(file);
} }
return result.trim(); return result.toString().trim();
} }
/** /**
@@ -191,23 +198,16 @@ public class ServerInfoUtils {
* @return {@link String} * @return {@link String}
*/ */
private static String getLinuxMainBoardSerial() { private static String getLinuxMainBoardSerial() {
String result = StrUtil.EMPTY; String command = "dmidecode | grep 'Serial Number' | awk '{print $3}' | tail -1";
String maniBordCmd = "dmidecode | grep 'Serial Number' | awk '{print $3}' | tail -1";
BufferedReader br = null;
try { try {
Process p = Runtime.getRuntime().exec(new String[] {"sh", "-c", maniBordCmd}); Process process = new ProcessBuilder("sh", "-c", command).start();
br = new BufferedReader(new InputStreamReader(p.getInputStream())); try (BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()))) {
String line; return reader.lines().findFirst().orElse(StrUtil.EMPTY);
while ((line = br.readLine()) != null) {
result += line;
break;
} }
} catch (IOException e) { } catch (IOException e) {
log.error("获取Linux主板信息错误 {}", e.getMessage()); log.error("获取 Linux 主板序列号失败: {}", e.getMessage());
} finally { return StrUtil.EMPTY;
IoUtil.close(br);
} }
return result;
} }
/** /**
@@ -216,7 +216,7 @@ public class ServerInfoUtils {
* @return {@link String} * @return {@link String}
*/ */
private static String getWindowMainBoardSerial() { private static String getWindowMainBoardSerial() {
String result = StrUtil.EMPTY; StringBuilder result = new StringBuilder(StrUtil.EMPTY);
File file = null; File file = null;
BufferedReader input = null; BufferedReader input = null;
try { try {
@@ -224,7 +224,15 @@ public class ServerInfoUtils {
file.deleteOnExit(); file.deleteOnExit();
FileWriter fw = new FileWriter(file); 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"; String vbs = """
Set objWMIService = GetObject("winmgmts:\\\\.\\root\\cimv2")
Set colItems = objWMIService.ExecQuery _
("Select * from Win32_BaseBoard")
For Each objItem in colItems
Wscript.Echo objItem.SerialNumber
exit for ' do the first cpu only!
Next
""";
fw.write(vbs); fw.write(vbs);
fw.close(); fw.close();
@@ -232,7 +240,7 @@ public class ServerInfoUtils {
input = new BufferedReader(new InputStreamReader(p.getInputStream())); input = new BufferedReader(new InputStreamReader(p.getInputStream()));
String line; String line;
while ((line = input.readLine()) != null) { while ((line = input.readLine()) != null) {
result += line; result.append(line);
} }
} catch (Exception e) { } catch (Exception e) {
log.error("获取Window主板信息错误 {}", e.getMessage()); log.error("获取Window主板信息错误 {}", e.getMessage());
@@ -240,7 +248,7 @@ public class ServerInfoUtils {
IoUtil.close(input); IoUtil.close(input);
FileUtil.del(file); FileUtil.del(file);
} }
return result.trim(); return result.toString().trim();
} }
/** /**

View File

@@ -36,8 +36,8 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController; import org.springframework.web.bind.annotation.RestController;
import top.continew.license.dto.LicenseCreatorParamVO; import top.continew.license.model.LicenseCreatorParamVO;
import top.continew.license.dto.LicenseExtraModel; import top.continew.license.model.LicenseExtraModel;
import top.continew.license.service.LicenseCreateService; import top.continew.license.service.LicenseCreateService;
import java.util.Calendar; import java.util.Calendar;

View File

@@ -11,25 +11,13 @@
<artifactId>continew-starter-license-generate</artifactId> <artifactId>continew-starter-license-generate</artifactId>
<packaging>jar</packaging> <packaging>jar</packaging>
<description>license 生成模块 基于 truelicens 实现</description> <description>ContiNew Starter License生成模块</description>
<dependencies> <dependencies>
<!--ContiNew Starter 日志模块 - 核心模块--> <!-- license 核心模块 -->
<dependency> <dependency>
<groupId>top.continew</groupId> <groupId>top.continew</groupId>
<artifactId>continew-starter-log-core</artifactId> <artifactId>continew-starter-license-core</artifactId>
</dependency>
<!-- license 依赖-->
<dependency>
<groupId>de.schlichtherle.truelicense</groupId>
<artifactId>truelicense-core</artifactId>
</dependency>
<!--zip4j压缩文件-->
<dependency>
<groupId>net.lingala.zip4j</groupId>
<artifactId>zip4j</artifactId>
</dependency> </dependency>
</dependencies> </dependencies>
</project> </project>

View File

@@ -32,7 +32,7 @@ import top.continew.starter.core.constant.PropertiesConstants;
* license 生成模块 自动配置 * license 生成模块 自动配置
* *
* @author loach * @author loach
* @since 1.2.0 * @since 2.11.0
*/ */
@AutoConfiguration @AutoConfiguration
@EnableConfigurationProperties(LicenseGenerateProperties.class) @EnableConfigurationProperties(LicenseGenerateProperties.class)

View File

@@ -23,7 +23,7 @@ import top.continew.starter.core.constant.PropertiesConstants;
* license 生成模块配置属性 * license 生成模块配置属性
* *
* @author Jasmine * @author Jasmine
* @since 1.2.0 * @since 2.11.0
*/ */
@ConfigurationProperties(PropertiesConstants.LICENSE_GENERATE) @ConfigurationProperties(PropertiesConstants.LICENSE_GENERATE)
public class LicenseGenerateProperties { public class LicenseGenerateProperties {

View File

@@ -1,59 +0,0 @@
/*
* 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;
}
}

View File

@@ -1,71 +0,0 @@
/*
* 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);
}
}

View File

@@ -18,16 +18,16 @@ package top.continew.license.manager;
import de.schlichtherle.license.*; import de.schlichtherle.license.*;
import de.schlichtherle.xml.GenericCertificate; import de.schlichtherle.xml.GenericCertificate;
import top.continew.license.exception.LicenseException;
import java.util.Date; import java.util.Date;
/** /**
* 自定义服务端证书管理类(生成证书) * 自定义服务端证书管理类(生成证书)
* *
* @Desc: * @author loach
* @Author loach * @author echo
* @ClassName top.continew.license.manager.ServerLicenseManager * @since 2.11.0
* @Date 2025-03-22 14:31
*/ */
public class ServerLicenseManager extends LicenseManager { public class ServerLicenseManager extends LicenseManager {
@@ -38,27 +38,31 @@ public class ServerLicenseManager extends LicenseManager {
/** /**
* 证书生成参数验证 * 证书生成参数验证
* *
* @param content * @param content 内容
* @throws LicenseContentException
*/ */
protected synchronized void validateCreate(final LicenseContent content) throws LicenseContentException { protected synchronized void validateCreate(final LicenseContent content) {
Date now = new Date(); Date now = new Date();
Date notBefore = content.getNotBefore(); Date notBefore = content.getNotBefore();
Date notAfter = content.getNotAfter(); Date notAfter = content.getNotAfter();
if (notBefore != null && now.before(notBefore)) { if (notBefore != null && now.before(notBefore)) {
throw new LicenseContentException("证书尚未生效,无法生成"); throw new LicenseException("证书尚未生效,无法生成");
} }
if (notAfter != null && now.after(notAfter)) { if (notAfter != null && now.after(notAfter)) {
throw new LicenseContentException("证书已过期,无法生成"); throw new LicenseException("证书已过期,无法生成");
} }
if (notBefore != null && notAfter != null && notBefore.after(notAfter)) { if (notBefore != null && notAfter != null && notBefore.after(notAfter)) {
throw new LicenseContentException("证书生效时间晚于失效时间,无法生成"); throw new LicenseException("证书生效时间晚于失效时间,无法生成");
} }
} }
/** /**
* 重写生成证书的方法,增加生成参数验证 * 重写生成证书的方法,增加生成参数验证
*
* @param content 内容
* @param notary 公证人
* @return {@link byte[] }
* @throws Exception 例外
*/ */
@Override @Override
protected synchronized byte[] create(LicenseContent content, LicenseNotary notary) throws Exception { protected synchronized byte[] create(LicenseContent content, LicenseNotary notary) throws Exception {

View File

@@ -16,62 +16,51 @@
package top.continew.license.service; package top.continew.license.service;
import cn.hutool.core.util.IdUtil;
import com.fasterxml.jackson.databind.ObjectMapper;
import de.schlichtherle.license.*;
import net.lingala.zip4j.ZipFile;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import top.continew.license.exception.LicenseException;
import top.continew.license.manager.ServerLicenseManager;
import top.continew.license.model.*;
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.File;
import java.io.FileOutputStream; import java.io.FileOutputStream;
import java.io.IOException; import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Date; import java.util.Date;
import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.prefs.Preferences; import java.util.prefs.Preferences;
import javax.security.auth.x500.X500Principal;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import com.fasterxml.jackson.databind.ObjectMapper;
import de.schlichtherle.license.CipherParam;
import de.schlichtherle.license.DefaultCipherParam;
import de.schlichtherle.license.DefaultLicenseParam;
import de.schlichtherle.license.KeyStoreParam;
import de.schlichtherle.license.LicenseContent;
import de.schlichtherle.license.LicenseManager;
import de.schlichtherle.license.LicenseParam;
import net.lingala.zip4j.ZipFile;
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;
/** /**
* 证书生成接口 实现类 * 证书生成接口 实现类
* *
* @Desc: * @author loach
* @Author loach * @since 2.11.0
* @ClassName top.continew.license.service.impl.LicenseCreateServiceImpl **/
* @Date 2025-03-22 18:36
*/
@Component
public class LicenseCreateService { public class LicenseCreateService {
private static final Logger log = LoggerFactory.getLogger(LicenseCreateService.class); private static final Logger log = LoggerFactory.getLogger(LicenseCreateService.class);
private static volatile LicenseCreateService instance; private static volatile LicenseCreateService instance;
private static final X500Principal DEFAULT_HOLDER_ISSUER = new X500Principal("CN=localhost, OU=localhost, O=localhost, L=SH, ST=SH, C=CN");
private LicenseCreateService() { private LicenseCreateService() {
// 私有构造
} }
/**
* 获取实例
*
* @return {@link LicenseCreateService }
*/
public static LicenseCreateService getInstance() { public static LicenseCreateService getInstance() {
if (instance == null) { if (instance == null) {
synchronized (LicenseCreateService.class) { synchronized (LicenseCreateService.class) {
@@ -83,135 +72,115 @@ public class LicenseCreateService {
return instance; return instance;
} }
private static final X500Principal DEFAULT_HOLDER_ISSUER = new X500Principal("CN=localhost, OU=localhost, O=localhost, L=SH, ST=SH, C=CN");
/** /**
* 获取服务器信息 * 获取服务器信息
* *
* @return * @return {@link LicenseExtraModel }
*/ */
public LicenseExtraModel getServerInfo() { public LicenseExtraModel getServerInfo() {
LicenseExtraModel serverInfos = ServerInfoUtils.getServerInfos(); return ServerInfoUtils.getServerInfos();
return serverInfos;
} }
/** /**
* 生成一个证书 * 生成一个证书
* *
* @param paramVO * @param paramVO param vo
* @throws Exception 例外
*/ */
public void generateLicense(LicenseCreatorParamVO paramVO) throws Exception { public void generateLicense(LicenseCreatorParamVO paramVO) throws Exception {
Map<String, Object> map = buildCreator(paramVO); BuildCreatorResp buildCreatorResp = buildCreator(paramVO);
LicenseCreatorParam param = (LicenseCreatorParam)map.get("creator"); LicenseCreatorParam param = buildCreatorResp.getParam();
ZipFile clientZipFile = (ZipFile)map.get("clientZipFile"); ZipFile clientZipFile = buildCreatorResp.getClientZipFile();
try { try {
LicenseParam licenseParam = initLicenseParam(param); LicenseParam licenseParam = initLicenseParam(param);
LicenseManager licenseManager = new ServerLicenseManager(licenseParam); LicenseManager licenseManager = new ServerLicenseManager(licenseParam);
LicenseContent licenseContent = initLcenseContent(param); LicenseContent licenseContent = initLicenseContent(param);
licenseManager.store(licenseContent, new File(param.getLicensePath())); licenseManager.store(licenseContent, new File(param.getLicensePath()));
log.info("{}证书生成成功", param.getSubject()); log.info("{} 证书生成成功 路径: {}", param.getSubject(), param.getLicensePath());
clientZipFile.addFile(param.getLicensePath()); clientZipFile.addFile(param.getLicensePath());
} catch (Exception e) { } catch (Exception e) {
e.printStackTrace(); throw new LicenseException("生成证书失败:" + param.getSubject(), e);
// log.error("{}生成证书失败:", param.getSubject());
throw new LicenseException("生成证书失败:" + param.getSubject());
} }
} }
/** /**
* 封装证书生成参数 * 构建 License 创建者对象及客户端配置压缩包。
*
* @param paramVO 创建参数封装对象,包含客户名、密码、描述、扩展信息等。
* @return Map 包含 LicenseCreatorParamcreator 和 生成的客户端 Zip 文件clientZipFile
* @throws Exception 命令执行或文件操作过程中出现异常
*/ */
private Map<String, Object> buildCreator(LicenseCreatorParamVO paramVO) throws Exception { private BuildCreatorResp buildCreator(LicenseCreatorParamVO paramVO) throws Exception {
String customerName = paramVO.getCustomerName(); String customerName = paramVO.getCustomerName();
String privateAlias = customerName + "-private-alias"; String privateAlias = customerName + "-private-alias";
String publicAlias = customerName + "-public-alias"; String publicAlias = customerName + "-public-alias";
String relativePath = relativePath(paramVO); String currentCustomerDir = relativePath(paramVO) + customerName + IdUtil.fastSimpleUUID() + File.separator;
String currentCustomerDir = relativePath + customerName + uuid() + File.separator; File customerDirFile = new File(currentCustomerDir);
File file = new File(currentCustomerDir); if (!customerDirFile.exists() && !customerDirFile.mkdirs()) {
if (!file.exists()) { throw new IOException("Failed to create directory: " + currentCustomerDir);
file.mkdirs();
} }
String privateKeystore = currentCustomerDir + "privateKeys.keystore"; String privateKeystore = currentCustomerDir + "privateKeys.keystore";
String publicKeystore = currentCustomerDir + "publicCerts.keystore"; String publicKeystore = currentCustomerDir + "publicCerts.keystore";
String certFilePath = currentCustomerDir + "certfile.cer";
String licensePath = currentCustomerDir + "license.lic";
LicenseCreatorParam param = new LicenseCreatorParam(); LicenseCreatorParam param = new LicenseCreatorParam();
param.setSubject(customerName); param.setSubject(customerName);
param.setPrivateAlias(privateAlias); param.setPrivateAlias(privateAlias);
param.setKeyPass(paramVO.getKeyPass()); param.setKeyPass(paramVO.getKeyPass());
param.setStorePass(paramVO.getStorePass()); param.setStorePass(paramVO.getStorePass());
param.setLicensePath(currentCustomerDir + "license.lic"); param.setLicensePath(licensePath);
param.setPrivateKeysStorePath(privateKeystore); param.setPrivateKeysStorePath(privateKeystore);
param.setExpiryTime(paramVO.getExpireTime()); param.setExpiryTime(paramVO.getExpireTime());
param.setDescription(paramVO.getDescription()); param.setDescription(paramVO.getDescription());
param.setLicenseExtraModel(paramVO.getLicenseExtraModel()); param.setLicenseExtraModel(paramVO.getLicenseExtraModel());
if (checkJavaVersion()) { int validity = getValidity(param.getIssuedTime(), paramVO.getExpireTime());
// JDK>=17 生成私钥库 String dname = "\"CN=localhost, OU=localhost, O=localhost, L=SH, ST=SH, C=CN\"";
String exe1 = "keytool -genkeypair -keyalg DSA -keysize 1024 -validity " + getValidity(param // 生成私钥库
.getIssuedTime(), paramVO String keyAlgOption = checkJavaVersion() ? "-keyalg DSA" : ""; // JDK>=17 要指定 keyalg
.getExpireTime()) + " -alias " + privateAlias + " -keystore " + privateKeystore + " -storepass " + paramVO String genKeyCmd = String
.getStorePass() + " -keypass " + paramVO .format("keytool -genkeypair %s -keysize 1024 -validity %d -alias %s -keystore %s -storepass %s -keypass %s -dname %s", keyAlgOption, validity, privateAlias, privateKeystore, paramVO
.getKeyPass() + " -dname \"CN=localhost, OU=localhost, O=localhost, L=SH, ST=SH, C=CN\""; .getStorePass(), paramVO.getKeyPass(), dname);
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(); String exportCertCmd = String
.format("keytool -exportcert -alias %s -keystore %s -storepass %s -file \"%s\"", privateAlias, privateKeystore, paramVO
.getStorePass(), certFilePath);
ExecCmdUtil.exec(exe1); // 导入到公钥库
ExecCmdUtil.exec(exe2); String importCertCmd = String
ExecCmdUtil.exec(exe3); .format("keytool -noprompt -import -alias %s -file \"%s\" -keystore %s -storepass %s", publicAlias, certFilePath, publicKeystore, paramVO
.getStorePass());
} else { // 执行命令
// JDK<17 生成私钥库 ExecCmdUtil.exec(genKeyCmd);
String exe1 = "keytool -genkeypair -keysize 1024 -validity " + getValidity(param.getIssuedTime(), paramVO ExecCmdUtil.exec(exportCertCmd);
.getExpireTime()) + " -alias " + privateAlias + " -keystore " + privateKeystore + " -storepass " + paramVO ExecCmdUtil.exec(importCertCmd);
.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); ZipFile clientZipFile = generateClientConfig(param, currentCustomerDir, publicAlias);
Map<String, Object> map = new HashMap<>(); return new BuildCreatorResp(param, clientZipFile);
map.put("creator", param);
map.put("clientZipFile", clientZipFile);
return map;
}
private String uuid() {
return UUID.randomUUID().toString().replace("-", "");
} }
/** /**
* 校验JDK版本 * 校验JDK版本
* *
* @return * @return boole T 17 版本 F 非 17 版本
* @throws Exception * @throws Exception 例外
*/ */
private boolean checkJavaVersion() throws Exception { private boolean checkJavaVersion() throws Exception {
String version = System.getProperty("java.version"); String version = System.getProperty("java.version");
int currentVersion = 0; int currentVersion = 0;
if (version.startsWith("1.")) { if (version.startsWith("1.")) {
currentVersion = Integer.parseInt(version.split("\\.")[1]); currentVersion = Integer.parseInt(version.split("\\.")[1]);
} else { } else {
currentVersion = Integer.parseInt(version.split("\\.")[0]); currentVersion = Integer.parseInt(version.split("\\.")[0]);
} }
return currentVersion >= 17;
if (currentVersion >= 17) {
return true;
} else {
return false;
}
} }
private ZipFile generateClientConfig(LicenseCreatorParam param, private ZipFile generateClientConfig(LicenseCreatorParam param,
@@ -229,17 +198,16 @@ public class LicenseCreateService {
FileOutputStream out = null; FileOutputStream out = null;
try { try {
out = new FileOutputStream(config); out = new FileOutputStream(config);
out.write(json.getBytes("UTF-8")); out.write(json.getBytes(StandardCharsets.UTF_8));
out.flush(); out.flush();
} catch (Exception e) { } catch (Exception e) {
e.printStackTrace(); throw new LicenseException("密钥文件生成失败", e);
throw new LicenseException("密钥文件生成失败");
} finally { } finally {
if (out != null) { if (out != null) {
try { try {
out.close(); out.close();
} catch (IOException e) { } catch (IOException e) {
e.printStackTrace(); throw new LicenseException("文件流关闭失败", e);
} }
} }
} }
@@ -250,28 +218,41 @@ public class LicenseCreateService {
return clientLicense; return clientLicense;
} }
//将有效时间转换成天 /**
* 将有效时间转换成天
*
* @param issuedTime 签发时间
* @param expireTime 过期时间
* @return int
*/
private int getValidity(Date issuedTime, Date expireTime) { private int getValidity(Date issuedTime, Date expireTime) {
long issued = issuedTime.getTime(); long issued = issuedTime.getTime();
long expire = expireTime.getTime(); long expire = expireTime.getTime();
long differ = expire - issued; long differ = expire - issued;
long remaining = differ % (24L * 3600L * 1000L); long remaining = differ % (24L * 3600L * 1000L);
Long validity = differ / (24L * 3600L * 1000L); long validity = differ / (24L * 3600L * 1000L);
if (remaining > 0) { if (remaining > 0) {
validity++; validity++;
} }
return validity.intValue(); return (int)validity;
} }
/**
* 是否是 Windows
*
* @return boolean
*/
private boolean isWindows() { private boolean isWindows() {
String os = System.getProperty("os.name"); String os = System.getProperty("os.name");
if (os.toLowerCase().contains("windows")) { return os.toLowerCase().contains("windows");
return true;
}
return false;
} }
//证书生成路径 /**
* 证书生成路径
*
* @param paramVO param vo
* @return {@link String }
*/
private String relativePath(LicenseCreatorParamVO paramVO) { private String relativePath(LicenseCreatorParamVO paramVO) {
if (paramVO.getLicenseSavePath() != null) { if (paramVO.getLicenseSavePath() != null) {
@@ -292,16 +273,17 @@ public class LicenseCreateService {
CipherParam cipherParam = new DefaultCipherParam(param.getStorePass()); CipherParam cipherParam = new DefaultCipherParam(param.getStorePass());
KeyStoreParam privateStoreParam = new CustomKeyStoreParam(LicenseCreateService.class, param KeyStoreParam privateStoreParam = new CustomKeyStoreParam(LicenseCreateService.class, param
.getPrivateKeysStorePath(), param.getPrivateAlias(), param.getStorePass(), param.getKeyPass()); .getPrivateKeysStorePath(), param.getPrivateAlias(), param.getStorePass(), param.getKeyPass());
LicenseParam licenseParam = new DefaultLicenseParam(param return new DefaultLicenseParam(param.getSubject(), preferences, privateStoreParam, cipherParam);
.getSubject(), preferences, privateStoreParam, cipherParam);
return licenseParam;
} }
/** /**
* 设置证书生成内容 * 设置证书生成内容
*
* @param param 参数
* @return {@link LicenseContent }
*/ */
private LicenseContent initLcenseContent(LicenseCreatorParam param) { private LicenseContent initLicenseContent(LicenseCreatorParam param) {
LicenseContent licenseContent = new LicenseContent(); LicenseContent licenseContent = new LicenseContent();
licenseContent.setHolder(DEFAULT_HOLDER_ISSUER); licenseContent.setHolder(DEFAULT_HOLDER_ISSUER);
@@ -314,7 +296,7 @@ public class LicenseCreateService {
licenseContent.setConsumerAmount(param.getConsumerAmount()); licenseContent.setConsumerAmount(param.getConsumerAmount());
licenseContent.setInfo(param.getDescription()); licenseContent.setInfo(param.getDescription());
if (param != null && param.getLicenseExtraModel() != null) { if (param.getLicenseExtraModel() != null) {
licenseContent.setExtra(param.getLicenseExtraModel()); licenseContent.setExtra(param.getLicenseExtraModel());
} }

View File

@@ -11,25 +11,14 @@
<artifactId>continew-starter-license-verify</artifactId> <artifactId>continew-starter-license-verify</artifactId>
<packaging>jar</packaging> <packaging>jar</packaging>
<description>license 校验模块 基于 truelicens 实现</description> <description>ContiNew Starter License校验模块</description>
<dependencies> <dependencies>
<!--ContiNew Starter 日志模块 - 核心模块--> <!-- license 核心模块 -->
<dependency> <dependency>
<groupId>top.continew</groupId> <groupId>top.continew</groupId>
<artifactId>continew-starter-log-core</artifactId> <artifactId>continew-starter-license-core</artifactId>
</dependency> </dependency>
<!-- license 依赖-->
<dependency>
<groupId>de.schlichtherle.truelicense</groupId>
<artifactId>truelicense-core</artifactId>
</dependency>
<!--zip4j压缩文件-->
<dependency>
<groupId>net.lingala.zip4j</groupId>
<artifactId>zip4j</artifactId>
</dependency>
</dependencies> </dependencies>
</project> </project>

View File

@@ -17,7 +17,6 @@
package top.continew.license.Initializing; package top.continew.license.Initializing;
import org.springframework.beans.factory.InitializingBean; import org.springframework.beans.factory.InitializingBean;
import top.continew.license.bean.LicenseInstallerBean; import top.continew.license.bean.LicenseInstallerBean;
/** /**
@@ -34,8 +33,7 @@ public class LicenseStarterInitializingBean implements InitializingBean {
} }
@Override @Override
public void afterPropertiesSet() throws Exception { public void afterPropertiesSet() {
// 安装证书,即校验客户机器参数是否符合证书要求,符合则安装成功,不符合则报错无法启动。
licenseInstallerBean.installLicense(); licenseInstallerBean.installLicense();
} }
} }

View File

@@ -35,7 +35,7 @@ import top.continew.starter.core.constant.PropertiesConstants;
* license 校验模块 自动配置 * license 校验模块 自动配置
* *
* @author loach * @author loach
* @since 1.2.0 * @since 2.11.0
*/ */
@AutoConfiguration @AutoConfiguration
@EnableConfigurationProperties(LicenseVerifyProperties.class) @EnableConfigurationProperties(LicenseVerifyProperties.class)
@@ -44,7 +44,12 @@ public class LicenseVerifyAutoConfiguration {
private static final Logger log = LoggerFactory.getLogger(LicenseVerifyAutoConfiguration.class); private static final Logger log = LoggerFactory.getLogger(LicenseVerifyAutoConfiguration.class);
/** 证书安装业务类 */ /**
* 证书安装业务类
*
* @param properties 属性
* @return {@link LicenseInstallerBean }
*/
@Bean @Bean
public LicenseInstallerBean licenseInstallerBean(LicenseVerifyProperties properties) { public LicenseInstallerBean licenseInstallerBean(LicenseVerifyProperties properties) {
return new LicenseInstallerBean(properties); return new LicenseInstallerBean(properties);
@@ -52,6 +57,9 @@ public class LicenseVerifyAutoConfiguration {
/** /**
* 启动校验 License服务 * 启动校验 License服务
*
* @param licenseInstallerBean 许可证安装程序bean
* @return {@link LicenseStarterInitializingBean }
*/ */
@Bean @Bean
@DependsOn("licenseInstallerBean") @DependsOn("licenseInstallerBean")
@@ -59,7 +67,12 @@ public class LicenseVerifyAutoConfiguration {
return new LicenseStarterInitializingBean(licenseInstallerBean); return new LicenseStarterInitializingBean(licenseInstallerBean);
} }
/** 客户端证书管理类(证书验证) */ /**
* 客户端证书管理类(证书验证)
*
* @param properties 属性
* @return {@link LicenseManager }
*/
@Bean @Bean
public LicenseManager licenseManager(LicenseVerifyProperties properties) { public LicenseManager licenseManager(LicenseVerifyProperties properties) {
return CustomLicenseManager.getInstance(properties); return CustomLicenseManager.getInstance(properties);

View File

@@ -25,8 +25,8 @@ import top.continew.starter.core.constant.PropertiesConstants;
* license 校验模块配置属性 * license 校验模块配置属性
* *
* @author loach * @author loach
* @since 1.2.0 * @since 2.11.0
*/ **/
@ConfigurationProperties(PropertiesConstants.LICENSE_VERIFY) @ConfigurationProperties(PropertiesConstants.LICENSE_VERIFY)
public class LicenseVerifyProperties { public class LicenseVerifyProperties {

View File

@@ -16,14 +16,16 @@
package top.continew.license.bean; package top.continew.license.bean;
import de.schlichtherle.license.*; import de.schlichtherle.license.LicenseContent;
import de.schlichtherle.license.LicenseManager;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import top.continew.license.autoconfigure.LicenseVerifyProperties; import top.continew.license.autoconfigure.LicenseVerifyProperties;
import top.continew.license.exception.VerifyException; import top.continew.license.exception.LicenseException;
import top.continew.license.manager.CustomLicenseManager; import top.continew.license.manager.CustomLicenseManager;
import java.io.*; import java.io.File;
import java.nio.file.Paths;
/** /**
* 证书安装业务类 * 证书安装业务类
@@ -35,44 +37,56 @@ public class LicenseInstallerBean {
private static final Logger log = LoggerFactory.getLogger(LicenseInstallerBean.class); private static final Logger log = LoggerFactory.getLogger(LicenseInstallerBean.class);
private final LicenseVerifyProperties properties;
private LicenseManager licenseManager; private LicenseManager licenseManager;
private LicenseVerifyProperties properties;
public LicenseInstallerBean(LicenseVerifyProperties properties) { public LicenseInstallerBean(LicenseVerifyProperties properties) {
this.properties = properties; this.properties = properties;
} }
// 安装证书 /**
public void installLicense() throws Exception { * 安装许可证
*/
public void installLicense() {
try { try {
licenseManager = CustomLicenseManager.getInstance(properties); this.licenseManager = CustomLicenseManager.getInstance(properties);
licenseManager.uninstall(); licenseManager.uninstall();
LicenseContent licenseContent = licenseManager.install(new File(properties File licenseFile = Paths.get(properties.getStorePath(), "clientLicense", "license.lic").toFile();
.getStorePath() + File.separator + "clientLicense/license.lic")); LicenseContent licenseContent = licenseManager.install(licenseFile);
log.info("证书认证通过,安装成功"); log.info("证书认证通过,安装成功: {}", licenseContent.getSubject());
} catch (Exception e) { } catch (Exception e) {
e.printStackTrace(); throw new LicenseException("证书认证失败", e);
throw new VerifyException("证书认证失败:" + e + " " + e.getMessage());
}
}
//卸载证书
public void uninstallLicense() throws Exception {
if (licenseManager != null) {
licenseManager.uninstall();
//Log.info("证书已卸载");
} }
} }
//即时验证证书合法性 /**
public void verify() throws Exception { * 卸载许可证
*/
public void uninstallLicense() {
if (licenseManager != null) { if (licenseManager != null) {
licenseManager.verify(); try {
//Log.info("证书认证通过"); licenseManager.uninstall();
log.info("证书已卸载");
} catch (Exception e) {
log.warn("卸载证书失败", e);
}
}
}
/**
* 即时验证证书合法性
*/
public void verify() {
if (licenseManager != null) {
try {
licenseManager.verify();
log.info("证书验证成功");
} catch (Exception e) {
throw new LicenseException("证书认证失败", e);
}
} else {
throw new LicenseException("证书认证失败: licenseManager is null");
} }
throw new VerifyException("证书认证失败:licenseManager is null");
} }
} }

View File

@@ -1,83 +0,0 @@
/*
* 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;
}
}

View File

@@ -1,32 +0,0 @@
/*
* 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);
}
}

View File

@@ -20,27 +20,30 @@ import com.fasterxml.jackson.databind.ObjectMapper;
import de.schlichtherle.license.*; import de.schlichtherle.license.*;
import de.schlichtherle.xml.GenericCertificate; import de.schlichtherle.xml.GenericCertificate;
import net.lingala.zip4j.ZipFile; import net.lingala.zip4j.ZipFile;
import net.lingala.zip4j.exception.ZipException;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import top.continew.license.bean.LicenseInstallerBean;
import top.continew.license.autoconfigure.LicenseVerifyProperties; import top.continew.license.autoconfigure.LicenseVerifyProperties;
import top.continew.license.dto.ConfigParam; import top.continew.license.bean.LicenseInstallerBean;
import top.continew.license.dto.LicenseExtraModel; import top.continew.license.exception.LicenseException;
import top.continew.license.keyStoreParam.CustomKeyStoreParam; import top.continew.license.model.ConfigParam;
import top.continew.license.utils.ServerInfoUtils; import top.continew.license.model.CustomKeyStoreParam;
import top.continew.license.model.LicenseExtraModel;
import top.continew.license.util.ServerInfoUtils;
import java.io.*; import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.prefs.Preferences; import java.util.prefs.Preferences;
/** /**
* 客户端证书管理类(证书验证) * 客户端证书管理类(证书验证)
* *
* @author loach * @author loach
* @since 1.2.0 * @since 2.11.0
*/ */
@Component
public class CustomLicenseManager extends LicenseManager { public class CustomLicenseManager extends LicenseManager {
private static final Logger log = LoggerFactory.getLogger(CustomLicenseManager.class); private static final Logger log = LoggerFactory.getLogger(CustomLicenseManager.class);
@@ -48,7 +51,7 @@ public class CustomLicenseManager extends LicenseManager {
private static volatile CustomLicenseManager INSTANCE; private static volatile CustomLicenseManager INSTANCE;
private LicenseExtraModel extraModel; private LicenseExtraModel extraModel;
private LicenseVerifyProperties properties; private final LicenseVerifyProperties properties;
private CustomLicenseManager(LicenseVerifyProperties properties) { private CustomLicenseManager(LicenseVerifyProperties properties) {
this.properties = properties; this.properties = properties;
@@ -87,67 +90,49 @@ public class CustomLicenseManager extends LicenseManager {
/** /**
* 解压压缩包 * 解压压缩包
*
* @throws ZipException
*/ */
private void extractZip() { private void extractZip() {
ZipFile config = new ZipFile(properties.getStorePath() + File.separator + "clientLicense.zip"); Path zipPath = Paths.get(properties.getStorePath(), "clientLicense.zip");
File licenseDir = new File(properties.getStorePath() + File.separator + "clientLicense"); Path outputDir = Paths.get(properties.getStorePath(), "clientLicense");
if (!licenseDir.exists()) {
licenseDir.mkdir(); try (ZipFile zipFile = new ZipFile(zipPath.toFile())) {
} if (!Files.exists(outputDir)) {
try { Files.createDirectories(outputDir);
config.extractAll(licenseDir.getAbsolutePath()); }
} catch (ZipException e) { zipFile.extractAll(outputDir.toAbsolutePath().toString());
log.error(e.getMessage()); } catch (IOException e) {
throw new RuntimeException(e); log.error("解压 clientLicense.zip 出错: {}", e.getMessage(), e);
throw new LicenseException("解压失败", e);
} }
} }
/** /**
* 获取压缩文件中配置的基础参数 * 获取压缩文件中配置的基础参数
* *
* @return * @return {@link ConfigParam }
* @throws Exception
*/ */
private ConfigParam getConfigParam() { private ConfigParam getConfigParam() {
FileInputStream config = null; Path configPath = Paths.get(properties.getStorePath(), "clientLicense", "clientConfig.json");
BufferedReader reader = null;
try { if (!Files.exists(configPath)) {
config = new FileInputStream(properties log.warn("配置文件不存在: {}", configPath);
.getStorePath() + File.separator + "clientLicense/clientConfig.json"); return null;
reader = new BufferedReader(new InputStreamReader(config, "UTF-8")); }
StringBuilder sb = new StringBuilder();
String temp = null; try (InputStream inputStream = Files.newInputStream(configPath)) {
while ((temp = reader.readLine()) != null) { ObjectMapper mapper = new ObjectMapper();
sb.append(temp); return mapper.readValue(inputStream, ConfigParam.class);
} } catch (IOException e) {
ObjectMapper mapper = new ObjectMapper(); log.error("读取配置文件失败: {}", e.getMessage(), e);
ConfigParam configParam = mapper.readValue(sb.toString(), ConfigParam.class); return null;
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;
} }
/** /**
* 重写验证证书方法,添加自定义参数验证 * 重写验证证书方法,添加自定义参数验证
*
* @param content 内容
* @throws LicenseContentException 许可证内容例外
*/ */
@Override @Override
protected synchronized void validate(LicenseContent content) throws LicenseContentException { protected synchronized void validate(LicenseContent content) throws LicenseContentException {
@@ -155,27 +140,31 @@ public class CustomLicenseManager extends LicenseManager {
super.validate(content); super.validate(content);
// 验证自定义参数 // 验证自定义参数
Object o = content.getExtra(); Object o = content.getExtra();
if (o != null && extraModel != null && o instanceof LicenseExtraModel) { if (extraModel != null && o instanceof LicenseExtraModel contentExtraModel) {
LicenseExtraModel contentExtraModel = (LicenseExtraModel)o;
if (!contentExtraModel.getCpuSerial().equals(extraModel.getCpuSerial())) { if (!contentExtraModel.getCpuSerial().equals(extraModel.getCpuSerial())) {
throw new LicenseContentException("CPU核数不匹配"); throw new LicenseException("CPU核数不匹配");
} }
if (!contentExtraModel.getMainBoardSerial().equals(extraModel.getMainBoardSerial())) { if (!contentExtraModel.getMainBoardSerial().equals(extraModel.getMainBoardSerial())) {
throw new LicenseContentException("主板序列号不匹配"); throw new LicenseException("主板序列号不匹配");
} }
if (!contentExtraModel.getIpAddress().equals(extraModel.getIpAddress())) { if (!contentExtraModel.getIpAddress().equals(extraModel.getIpAddress())) {
throw new LicenseContentException("IP地址不匹配"); throw new LicenseException("IP地址不匹配");
} }
if (!contentExtraModel.getMacAddress().equals(extraModel.getMacAddress())) { if (!contentExtraModel.getMacAddress().equals(extraModel.getMacAddress())) {
throw new LicenseContentException("MAC地址不匹配"); throw new LicenseException("MAC地址不匹配");
} }
} else { } else {
throw new LicenseContentException("证书无效"); throw new LicenseException("证书无效");
} }
} }
/** /**
* 重写证书安装方法,主要是更改调用本类的验证方法 * 重写证书安装方法,主要是更改调用本类的验证方法
*
* @param key 钥匙
* @param notary 公证人
* @return {@link LicenseContent }
* @throws Exception 例外
*/ */
@Override @Override
protected synchronized LicenseContent install(final byte[] key, LicenseNotary notary) throws Exception { protected synchronized LicenseContent install(final byte[] key, LicenseNotary notary) throws Exception {
@@ -192,23 +181,31 @@ public class CustomLicenseManager extends LicenseManager {
/** /**
* 重写验证证书合法的方法,主要是更改调用本类的验证方法 * 重写验证证书合法的方法,主要是更改调用本类的验证方法
*
* @param notary 公证人
* @return {@link LicenseContent }
* @throws Exception 例外
*/ */
@Override @Override
protected synchronized LicenseContent verify(LicenseNotary notary) throws Exception { protected synchronized LicenseContent verify(LicenseNotary notary) throws Exception {
GenericCertificate certificate = getCertificate(); GenericCertificate certificate = getCertificate();
if (null != certificate) if (certificate != null) {
return (LicenseContent)certificate.getContent(); return (LicenseContent)certificate.getContent();
}
// Load license key from preferences, byte[] licenseKey = getLicenseKey();
final byte[] key = getLicenseKey(); if (licenseKey == null) {
if (null == key) String subject = getLicenseParam().getSubject();
throw new NoLicenseInstalledException(getLicenseParam().getSubject()); throw new NoLicenseInstalledException(subject);
certificate = getPrivacyGuard().key2cert(key); }
// 使用私钥解密生成证书
certificate = getPrivacyGuard().key2cert(licenseKey);
// 验证证书签名
notary.verify(certificate); notary.verify(certificate);
final LicenseContent content = (LicenseContent)certificate.getContent(); // 提取内容并进行业务校验
LicenseContent content = (LicenseContent)certificate.getContent();
this.validate(content); this.validate(content);
// 缓存证书
setCertificate(certificate); setCertificate(certificate);
return content; return content;
} }
} }

View File

@@ -1,338 +0,0 @@
/*
* 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;
}
}

View File

@@ -11,17 +11,10 @@
<artifactId>continew-starter-license</artifactId> <artifactId>continew-starter-license</artifactId>
<packaging>pom</packaging> <packaging>pom</packaging>
<description>ContiNew Starter license 管理模块</description> <description>ContiNew Starter License模块</description>
<modules> <modules>
<module>continew-starter-license-core</module>
<module>continew-starter-license-generate</module> <module>continew-starter-license-generate</module>
<module>continew-starter-license-verify</module> <module>continew-starter-license-verify</module>
</modules> </modules>
<dependencies>
<!-- 核心模块 -->
<dependency>
<groupId>top.continew</groupId>
<artifactId>continew-starter-core</artifactId>
</dependency>
</dependencies>
</project> </project>