feat(license): 新增 License 模块

This commit is contained in:
Gyq灬明
2025-04-17 01:08:08 +00:00
committed by Charles7c
parent 5129fea34d
commit da4c8154bf
30 changed files with 2715 additions and 0 deletions

View File

@@ -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. 配置YMLlicense 压缩包存放位置)
```yaml
license:
savePath: D:/license/
```
注:默认加载 `D:/license/` 位置。

View File

@@ -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>

View File

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

View File

@@ -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;
}
}

View File

@@ -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);
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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);
}
}

View File

@@ -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);
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -0,0 +1,2 @@
top.continew.license.config.LicenseAutoConfiguration
top.continew.license.Initializing.LicenseStarterInitializingBean

View File

@@ -0,0 +1,2 @@
license:
savePath: C:/license/