mirror of
https://github.com/continew-org/continew-starter.git
synced 2025-11-06 06:57:41 +08:00
feat(license): 增加核心模块,优化代码结构
This commit is contained in:
@@ -11,25 +11,14 @@
|
||||
|
||||
<artifactId>continew-starter-license-verify</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
<description>license 校验模块 基于 truelicens 实现</description>
|
||||
<description>ContiNew Starter License校验模块</description>
|
||||
|
||||
<dependencies>
|
||||
<!--ContiNew Starter 日志模块 - 核心模块-->
|
||||
<!-- license 核心模块 -->
|
||||
<dependency>
|
||||
<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>
|
||||
</dependencies>
|
||||
</project>
|
||||
@@ -17,7 +17,6 @@
|
||||
package top.continew.license.Initializing;
|
||||
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
|
||||
import top.continew.license.bean.LicenseInstallerBean;
|
||||
|
||||
/**
|
||||
@@ -34,8 +33,7 @@ public class LicenseStarterInitializingBean implements InitializingBean {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterPropertiesSet() throws Exception {
|
||||
// 安装证书,即校验客户机器参数是否符合证书要求,符合则安装成功,不符合则报错无法启动。
|
||||
public void afterPropertiesSet() {
|
||||
licenseInstallerBean.installLicense();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -35,7 +35,7 @@ import top.continew.starter.core.constant.PropertiesConstants;
|
||||
* license 校验模块 自动配置
|
||||
*
|
||||
* @author loach
|
||||
* @since 1.2.0
|
||||
* @since 2.11.0
|
||||
*/
|
||||
@AutoConfiguration
|
||||
@EnableConfigurationProperties(LicenseVerifyProperties.class)
|
||||
@@ -44,7 +44,12 @@ public class LicenseVerifyAutoConfiguration {
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(LicenseVerifyAutoConfiguration.class);
|
||||
|
||||
/** 证书安装业务类 */
|
||||
/**
|
||||
* 证书安装业务类
|
||||
*
|
||||
* @param properties 属性
|
||||
* @return {@link LicenseInstallerBean }
|
||||
*/
|
||||
@Bean
|
||||
public LicenseInstallerBean licenseInstallerBean(LicenseVerifyProperties properties) {
|
||||
return new LicenseInstallerBean(properties);
|
||||
@@ -52,6 +57,9 @@ public class LicenseVerifyAutoConfiguration {
|
||||
|
||||
/**
|
||||
* 启动校验 License服务
|
||||
*
|
||||
* @param licenseInstallerBean 许可证安装程序bean
|
||||
* @return {@link LicenseStarterInitializingBean }
|
||||
*/
|
||||
@Bean
|
||||
@DependsOn("licenseInstallerBean")
|
||||
@@ -59,7 +67,12 @@ public class LicenseVerifyAutoConfiguration {
|
||||
return new LicenseStarterInitializingBean(licenseInstallerBean);
|
||||
}
|
||||
|
||||
/** 客户端证书管理类(证书验证) */
|
||||
/**
|
||||
* 客户端证书管理类(证书验证)
|
||||
*
|
||||
* @param properties 属性
|
||||
* @return {@link LicenseManager }
|
||||
*/
|
||||
@Bean
|
||||
public LicenseManager licenseManager(LicenseVerifyProperties properties) {
|
||||
return CustomLicenseManager.getInstance(properties);
|
||||
|
||||
@@ -25,8 +25,8 @@ import top.continew.starter.core.constant.PropertiesConstants;
|
||||
* license 校验模块配置属性
|
||||
*
|
||||
* @author loach
|
||||
* @since 1.2.0
|
||||
*/
|
||||
* @since 2.11.0
|
||||
**/
|
||||
@ConfigurationProperties(PropertiesConstants.LICENSE_VERIFY)
|
||||
public class LicenseVerifyProperties {
|
||||
|
||||
|
||||
@@ -16,14 +16,16 @@
|
||||
|
||||
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.LoggerFactory;
|
||||
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 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 final LicenseVerifyProperties properties;
|
||||
private LicenseManager licenseManager;
|
||||
|
||||
private LicenseVerifyProperties properties;
|
||||
|
||||
public LicenseInstallerBean(LicenseVerifyProperties properties) {
|
||||
this.properties = properties;
|
||||
}
|
||||
|
||||
// 安装证书
|
||||
public void installLicense() throws Exception {
|
||||
/**
|
||||
* 安装许可证
|
||||
*/
|
||||
public void installLicense() {
|
||||
try {
|
||||
licenseManager = CustomLicenseManager.getInstance(properties);
|
||||
this.licenseManager = CustomLicenseManager.getInstance(properties);
|
||||
licenseManager.uninstall();
|
||||
LicenseContent licenseContent = licenseManager.install(new File(properties
|
||||
.getStorePath() + File.separator + "clientLicense/license.lic"));
|
||||
log.info("证书认证通过,安装成功");
|
||||
File licenseFile = Paths.get(properties.getStorePath(), "clientLicense", "license.lic").toFile();
|
||||
LicenseContent licenseContent = licenseManager.install(licenseFile);
|
||||
log.info("证书认证通过,安装成功: {}", licenseContent.getSubject());
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
throw new VerifyException("证书认证失败:" + e + " " + e.getMessage());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//卸载证书
|
||||
public void uninstallLicense() throws Exception {
|
||||
if (licenseManager != null) {
|
||||
licenseManager.uninstall();
|
||||
//Log.info("证书已卸载");
|
||||
throw new LicenseException("证书认证失败", e);
|
||||
}
|
||||
}
|
||||
|
||||
//即时验证证书合法性
|
||||
public void verify() throws Exception {
|
||||
/**
|
||||
* 卸载许可证
|
||||
*/
|
||||
public void uninstallLicense() {
|
||||
if (licenseManager != null) {
|
||||
licenseManager.verify();
|
||||
//Log.info("证书认证通过");
|
||||
try {
|
||||
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");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,65 +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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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-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);
|
||||
}
|
||||
}
|
||||
@@ -20,27 +20,30 @@ 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.autoconfigure.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 top.continew.license.bean.LicenseInstallerBean;
|
||||
import top.continew.license.exception.LicenseException;
|
||||
import top.continew.license.model.ConfigParam;
|
||||
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;
|
||||
|
||||
/**
|
||||
* 客户端证书管理类(证书验证)
|
||||
*
|
||||
* @author loach
|
||||
* @since 1.2.0
|
||||
* @since 2.11.0
|
||||
*/
|
||||
@Component
|
||||
public class CustomLicenseManager extends LicenseManager {
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(CustomLicenseManager.class);
|
||||
@@ -48,7 +51,7 @@ public class CustomLicenseManager extends LicenseManager {
|
||||
private static volatile CustomLicenseManager INSTANCE;
|
||||
private LicenseExtraModel extraModel;
|
||||
|
||||
private LicenseVerifyProperties properties;
|
||||
private final LicenseVerifyProperties properties;
|
||||
|
||||
private CustomLicenseManager(LicenseVerifyProperties properties) {
|
||||
this.properties = properties;
|
||||
@@ -87,67 +90,49 @@ public class CustomLicenseManager extends LicenseManager {
|
||||
|
||||
/**
|
||||
* 解压压缩包
|
||||
*
|
||||
* @throws ZipException
|
||||
*/
|
||||
private void extractZip() {
|
||||
ZipFile config = new ZipFile(properties.getStorePath() + File.separator + "clientLicense.zip");
|
||||
File licenseDir = new File(properties.getStorePath() + File.separator + "clientLicense");
|
||||
if (!licenseDir.exists()) {
|
||||
licenseDir.mkdir();
|
||||
}
|
||||
try {
|
||||
config.extractAll(licenseDir.getAbsolutePath());
|
||||
} catch (ZipException e) {
|
||||
log.error(e.getMessage());
|
||||
throw new RuntimeException(e);
|
||||
Path zipPath = Paths.get(properties.getStorePath(), "clientLicense.zip");
|
||||
Path outputDir = Paths.get(properties.getStorePath(), "clientLicense");
|
||||
|
||||
try (ZipFile zipFile = new ZipFile(zipPath.toFile())) {
|
||||
if (!Files.exists(outputDir)) {
|
||||
Files.createDirectories(outputDir);
|
||||
}
|
||||
zipFile.extractAll(outputDir.toAbsolutePath().toString());
|
||||
} catch (IOException e) {
|
||||
log.error("解压 clientLicense.zip 出错: {}", e.getMessage(), e);
|
||||
throw new LicenseException("解压失败", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取压缩文件中配置的基础参数
|
||||
*
|
||||
* @return
|
||||
* @throws Exception
|
||||
* @return {@link ConfigParam }
|
||||
*/
|
||||
private ConfigParam getConfigParam() {
|
||||
FileInputStream config = null;
|
||||
BufferedReader reader = null;
|
||||
try {
|
||||
config = new FileInputStream(properties
|
||||
.getStorePath() + File.separator + "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);
|
||||
}
|
||||
}
|
||||
Path configPath = Paths.get(properties.getStorePath(), "clientLicense", "clientConfig.json");
|
||||
|
||||
if (!Files.exists(configPath)) {
|
||||
log.warn("配置文件不存在: {}", configPath);
|
||||
return null;
|
||||
}
|
||||
|
||||
try (InputStream inputStream = Files.newInputStream(configPath)) {
|
||||
ObjectMapper mapper = new ObjectMapper();
|
||||
return mapper.readValue(inputStream, ConfigParam.class);
|
||||
} catch (IOException e) {
|
||||
log.error("读取配置文件失败: {}", e.getMessage(), e);
|
||||
return null;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 重写验证证书方法,添加自定义参数验证
|
||||
*
|
||||
* @param content 内容
|
||||
* @throws LicenseContentException 许可证内容例外
|
||||
*/
|
||||
@Override
|
||||
protected synchronized void validate(LicenseContent content) throws LicenseContentException {
|
||||
@@ -155,27 +140,31 @@ public class CustomLicenseManager extends LicenseManager {
|
||||
super.validate(content);
|
||||
// 验证自定义参数
|
||||
Object o = content.getExtra();
|
||||
if (o != null && extraModel != null && o instanceof LicenseExtraModel) {
|
||||
LicenseExtraModel contentExtraModel = (LicenseExtraModel)o;
|
||||
if (extraModel != null && o instanceof LicenseExtraModel contentExtraModel) {
|
||||
if (!contentExtraModel.getCpuSerial().equals(extraModel.getCpuSerial())) {
|
||||
throw new LicenseContentException("CPU核数不匹配");
|
||||
throw new LicenseException("CPU核数不匹配");
|
||||
}
|
||||
if (!contentExtraModel.getMainBoardSerial().equals(extraModel.getMainBoardSerial())) {
|
||||
throw new LicenseContentException("主板序列号不匹配");
|
||||
throw new LicenseException("主板序列号不匹配");
|
||||
}
|
||||
if (!contentExtraModel.getIpAddress().equals(extraModel.getIpAddress())) {
|
||||
throw new LicenseContentException("IP地址不匹配");
|
||||
throw new LicenseException("IP地址不匹配");
|
||||
}
|
||||
if (!contentExtraModel.getMacAddress().equals(extraModel.getMacAddress())) {
|
||||
throw new LicenseContentException("MAC地址不匹配");
|
||||
throw new LicenseException("MAC地址不匹配");
|
||||
}
|
||||
} else {
|
||||
throw new LicenseContentException("证书无效");
|
||||
throw new LicenseException("证书无效");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 重写证书安装方法,主要是更改调用本类的验证方法
|
||||
*
|
||||
* @param key 钥匙
|
||||
* @param notary 公证人
|
||||
* @return {@link LicenseContent }
|
||||
* @throws Exception 例外
|
||||
*/
|
||||
@Override
|
||||
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
|
||||
protected synchronized LicenseContent verify(LicenseNotary notary) throws Exception {
|
||||
GenericCertificate certificate = getCertificate();
|
||||
if (null != certificate)
|
||||
if (certificate != null) {
|
||||
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);
|
||||
}
|
||||
byte[] licenseKey = getLicenseKey();
|
||||
if (licenseKey == null) {
|
||||
String subject = getLicenseParam().getSubject();
|
||||
throw new NoLicenseInstalledException(subject);
|
||||
}
|
||||
// 使用私钥解密生成证书
|
||||
certificate = getPrivacyGuard().key2cert(licenseKey);
|
||||
// 验证证书签名
|
||||
notary.verify(certificate);
|
||||
final LicenseContent content = (LicenseContent)certificate.getContent();
|
||||
// 提取内容并进行业务校验
|
||||
LicenseContent content = (LicenseContent)certificate.getContent();
|
||||
this.validate(content);
|
||||
// 缓存证书
|
||||
setCertificate(certificate);
|
||||
|
||||
return content;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user