init project
This commit is contained in:
88
.gitignore
vendored
Normal file
88
.gitignore
vendored
Normal file
@@ -0,0 +1,88 @@
|
||||
######################################################################
|
||||
# Build Tools
|
||||
|
||||
.gradle
|
||||
/build/
|
||||
!gradle/wrapper/gradle-wrapper.jar
|
||||
|
||||
target/
|
||||
!.mvn/wrapper/maven-wrapper.jar
|
||||
|
||||
######################################################################
|
||||
# IDE
|
||||
|
||||
### STS ###
|
||||
.apt_generated
|
||||
.classpath
|
||||
.factorypath
|
||||
.project
|
||||
.settings
|
||||
.springBeans
|
||||
|
||||
### IntelliJ IDEA ###
|
||||
.idea
|
||||
*.iws
|
||||
*.iml
|
||||
*.ipr
|
||||
|
||||
### JRebel ###
|
||||
rebel.xml
|
||||
### NetBeans ###
|
||||
nbproject/private/
|
||||
build/*
|
||||
nbbuild/
|
||||
dist/
|
||||
nbdist/
|
||||
.nb-gradle/
|
||||
|
||||
######################################################################
|
||||
# Others
|
||||
*.log
|
||||
*.xml.versionsBackup
|
||||
*.swp
|
||||
*.bak
|
||||
tmp
|
||||
|
||||
!*/build/*.java
|
||||
!*/build/*.html
|
||||
!*/build/*.xml
|
||||
|
||||
# docker
|
||||
docker/mysql/data
|
||||
docker/redis/data
|
||||
docker/mysql/log
|
||||
docker/nacos/logs
|
||||
docker/mysql/db/*.sql
|
||||
docker/wfc/*/jar/*.jar
|
||||
docker/wfc/*/*/jar/*.jar
|
||||
|
||||
# Logs
|
||||
logs
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
pnpm-debug.log*
|
||||
lerna-debug.log*
|
||||
|
||||
node_modules
|
||||
.DS_Store
|
||||
dist
|
||||
dist-ssr
|
||||
coverage
|
||||
*.local
|
||||
|
||||
# Editor directories and files
|
||||
.vscode/*
|
||||
!.vscode/extensions.json
|
||||
!.vscode/settings.json
|
||||
!.vscode/launch.json
|
||||
*.suo
|
||||
*.ntvs*
|
||||
*.njsproj
|
||||
*.sln
|
||||
*.sw?
|
||||
|
||||
package-lock.json
|
||||
yarn.lock
|
||||
|
||||
.VSCodeCounter
|
||||
178
pom.xml
Normal file
178
pom.xml
Normal file
@@ -0,0 +1,178 @@
|
||||
<?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>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-parent</artifactId>
|
||||
<version>2.3.0.RELEASE</version>
|
||||
<relativePath/> <!-- lookup parent from repository -->
|
||||
</parent>
|
||||
|
||||
<groupId>com.modules</groupId>
|
||||
<artifactId>cloud-license-serve</artifactId>
|
||||
<version>1.0.0</version>
|
||||
<name>cloud-license-serve</name>
|
||||
<description>license serve project</description>
|
||||
|
||||
<properties>
|
||||
<java.version>1.8</java.version>
|
||||
<spring-cloud.version>Hoxton.SR3</spring-cloud.version>
|
||||
</properties>
|
||||
<dependencies>
|
||||
|
||||
<!-- SpringBoot 核心包 -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-thymeleaf</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-devtools</artifactId>
|
||||
<scope>runtime</scope>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
|
||||
<!-- SpringBoot 测试 -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-test</artifactId>
|
||||
<scope>test</scope>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>org.junit.vintage</groupId>
|
||||
<artifactId>junit-vintage-engine</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
|
||||
<!-- lombok -->
|
||||
<dependency>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- License -->
|
||||
<dependency>
|
||||
<groupId>de.schlichtherle.truelicense</groupId>
|
||||
<artifactId>truelicense-core</artifactId>
|
||||
<version>1.33</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>net.sourceforge.nekohtml</groupId>
|
||||
<artifactId>nekohtml</artifactId>
|
||||
<version>1.9.18</version>
|
||||
</dependency>
|
||||
|
||||
<!-- Commons -->
|
||||
<dependency>
|
||||
<groupId>org.apache.commons</groupId>
|
||||
<artifactId>commons-lang3</artifactId>
|
||||
<version>3.7</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>commons-io</groupId>
|
||||
<artifactId>commons-io</artifactId>
|
||||
<version>2.6</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>commons-codec</groupId>
|
||||
<artifactId>commons-codec</artifactId>
|
||||
<version>1.11</version>
|
||||
</dependency>
|
||||
|
||||
<!-- Apache HttpClient -->
|
||||
<dependency>
|
||||
<groupId>org.apache.httpcomponents</groupId>
|
||||
<artifactId>httpclient</artifactId>
|
||||
<version>4.5.5</version>
|
||||
</dependency>
|
||||
|
||||
<!-- Fastjson -->
|
||||
<dependency>
|
||||
<groupId>com.alibaba</groupId>
|
||||
<artifactId>fastjson</artifactId>
|
||||
<version>1.2.47</version>
|
||||
</dependency>
|
||||
|
||||
<!-- Java Servlet API -->
|
||||
<dependency>
|
||||
<groupId>javax.servlet</groupId>
|
||||
<artifactId>javax.servlet-api</artifactId>
|
||||
<version>4.0.1</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
<!-- Jackson对自动解析JSON和XML格式的支持 -->
|
||||
<dependency>
|
||||
<groupId>com.fasterxml.jackson.jaxrs</groupId>
|
||||
<artifactId>jackson-jaxrs-json-provider</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.fasterxml.jackson.dataformat</groupId>
|
||||
<artifactId>jackson-dataformat-xml</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-test</artifactId>
|
||||
<scope>test</scope>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>org.junit.vintage</groupId>
|
||||
<artifactId>junit-vintage-engine</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
<!-- 依赖版本管理 -->
|
||||
<dependencyManagement>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-dependencies</artifactId>
|
||||
<version>${spring-cloud.version}</version>
|
||||
<type>pom</type>
|
||||
<scope>import</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</dependencyManagement>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-maven-plugin</artifactId>
|
||||
<!-- 引入系统范围内的依赖 -->
|
||||
<configuration>
|
||||
<includeSystemScope>true</includeSystemScope>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<!--添加配置跳过测试-->
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-surefire-plugin</artifactId>
|
||||
<version>2.22.2</version>
|
||||
<configuration>
|
||||
<skipTests>true</skipTests>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
</project>
|
||||
18
src/main/java/org/wfc/common/license/ServeApplication.java
Normal file
18
src/main/java/org/wfc/common/license/ServeApplication.java
Normal file
@@ -0,0 +1,18 @@
|
||||
package org.wfc.common.license;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.boot.web.servlet.ServletComponentScan;
|
||||
|
||||
@Slf4j
|
||||
@SpringBootApplication
|
||||
@ServletComponentScan
|
||||
public class ServeApplication {
|
||||
|
||||
public static void main(String[] args) {
|
||||
log.info("启动开始");
|
||||
SpringApplication.run(ServeApplication.class, args);
|
||||
log.info("启动成功");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
package org.wfc.common.license.common.config;
|
||||
|
||||
import org.apache.http.Header;
|
||||
import org.apache.http.client.HttpClient;
|
||||
import org.apache.http.impl.client.DefaultConnectionKeepAliveStrategy;
|
||||
import org.apache.http.impl.client.DefaultHttpRequestRetryHandler;
|
||||
import org.apache.http.impl.client.HttpClientBuilder;
|
||||
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
|
||||
import org.apache.http.message.BasicHeader;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.http.client.ClientHttpRequestFactory;
|
||||
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
@Configuration
|
||||
public class RestTemplateConfig {
|
||||
|
||||
/**
|
||||
* 返回RestTemplate
|
||||
*
|
||||
* @param factory ClientHttpRequestFactory
|
||||
* @return RestTemplate
|
||||
*/
|
||||
@Bean
|
||||
public RestTemplate restTemplate(ClientHttpRequestFactory factory) {
|
||||
return new RestTemplate(factory);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* ClientHttpRequestFactory接口的另一种实现方式(推荐使用),即:
|
||||
* HttpComponentsClientHttpRequestFactory:底层使用Httpclient连接池的方式创建Http连接请求
|
||||
*
|
||||
* @return HttpComponentsClientHttpRequestFactory
|
||||
*/
|
||||
@Bean
|
||||
public HttpComponentsClientHttpRequestFactory httpComponentsClientHttpRequestFactory() {
|
||||
//Httpclient连接池,长连接保持30秒
|
||||
PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(30, TimeUnit.SECONDS);
|
||||
|
||||
//设置总连接数
|
||||
connectionManager.setMaxTotal(1000);
|
||||
//设置同路由的并发数
|
||||
connectionManager.setDefaultMaxPerRoute(1000);
|
||||
|
||||
//设置header
|
||||
List<Header> headers = new ArrayList<Header>();
|
||||
headers.add(new BasicHeader("User-Agent", "Mozilla/5.0 (Windows NT 6.3; rv:36.0) Gecko/20100101 Firefox/36.04"));
|
||||
headers.add(new BasicHeader("Accept-Encoding", "gzip, deflate"));
|
||||
headers.add(new BasicHeader("Accept-Language", "zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3"));
|
||||
headers.add(new BasicHeader("Connection", "keep-alive"));
|
||||
|
||||
//创建HttpClient
|
||||
HttpClient httpClient = HttpClientBuilder.create()
|
||||
.setConnectionManager(connectionManager)
|
||||
.setDefaultHeaders(headers)
|
||||
.setRetryHandler(new DefaultHttpRequestRetryHandler(3, true)) //设置重试次数
|
||||
.setKeepAliveStrategy(new DefaultConnectionKeepAliveStrategy()) //设置保持长连接
|
||||
.build();
|
||||
|
||||
//创建HttpComponentsClientHttpRequestFactory实例
|
||||
HttpComponentsClientHttpRequestFactory requestFactory =
|
||||
new HttpComponentsClientHttpRequestFactory(httpClient);
|
||||
|
||||
//设置客户端和服务端建立连接的超时时间
|
||||
requestFactory.setConnectTimeout(5000);
|
||||
//设置客户端从服务端读取数据的超时时间
|
||||
requestFactory.setReadTimeout(5000);
|
||||
//设置从连接池获取连接的超时时间,不宜过长
|
||||
requestFactory.setConnectionRequestTimeout(200);
|
||||
//缓冲请求数据,默认为true。通过POST或者PUT大量发送数据时,建议将此更改为false,以免耗尽内存
|
||||
requestFactory.setBufferRequestBody(false);
|
||||
return requestFactory;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
package org.wfc.common.license.common.utils;
|
||||
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.ApplicationContextAware;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.web.context.request.RequestContextHolder;
|
||||
import org.springframework.web.context.request.ServletRequestAttributes;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 自定义Spring工具类
|
||||
*/
|
||||
@Component
|
||||
public class SpringContextUtils implements ApplicationContextAware {
|
||||
private static ApplicationContext applicationContext;
|
||||
|
||||
@Override
|
||||
public void setApplicationContext(ApplicationContext context) throws BeansException {
|
||||
applicationContext = context;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取ApplicationContext对象
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public static ApplicationContext getApplicationContext() {
|
||||
return applicationContext;
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据bean的名称获取bean
|
||||
*
|
||||
* @param name
|
||||
* @return
|
||||
*/
|
||||
public static Object getBeanByName(String name) {
|
||||
return applicationContext.getBean(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据bean的class来查找对象
|
||||
*
|
||||
* @param <T>
|
||||
* @param c
|
||||
* @return
|
||||
*/
|
||||
public static <T> T getBeanByClass(Class<T> c) {
|
||||
return applicationContext.getBean(c);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据bean的class来查找所有的对象(包括子类)
|
||||
*
|
||||
* @param <T>
|
||||
* @param c
|
||||
* @return
|
||||
*/
|
||||
public static <T> Map<String, T> getBeansByClass(Class<T> c) {
|
||||
return applicationContext.getBeansOfType(c);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取HttpServletRequest
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public static HttpServletRequest getRequest() {
|
||||
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.currentRequestAttributes();
|
||||
HttpServletRequest request = attributes.getRequest();
|
||||
return request;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,139 @@
|
||||
package org.wfc.common.license.controller;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import org.wfc.common.license.domain.LicenseCheckModel;
|
||||
import org.wfc.common.license.domain.LicenseCreatorParam;
|
||||
import org.wfc.common.license.domain.LicenseGenParam;
|
||||
import org.wfc.common.license.license.AbstractServerInfos;
|
||||
import org.wfc.common.license.license.LicenseCreator;
|
||||
import org.wfc.common.license.license.LinuxServerInfos;
|
||||
import org.wfc.common.license.license.WindowsServerInfos;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 用于生成证书文件,不能放在给客户部署的代码里
|
||||
*
|
||||
* @since 1.0.0
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/license")
|
||||
public class LicenseCreatorController {
|
||||
|
||||
/**
|
||||
* 证书生成路径
|
||||
*/
|
||||
@Value("${license.licensePath}")
|
||||
private String licensePath;
|
||||
|
||||
/**
|
||||
* 获取服务器硬件信息
|
||||
*
|
||||
* @param osName 操作系统类型,如果为空则自动判断
|
||||
* @return com.ccx.models.license.LicenseCheckModel
|
||||
*/
|
||||
@RequestMapping(value = "/getServerInfos", produces = {MediaType.APPLICATION_JSON_UTF8_VALUE})
|
||||
public LicenseCheckModel getServerInfos(@RequestParam(value = "osName", required = false) String osName) {
|
||||
//操作系统类型
|
||||
if (StringUtils.isBlank(osName)) {
|
||||
osName = System.getProperty("os.name");
|
||||
}
|
||||
osName = osName.toLowerCase();
|
||||
|
||||
AbstractServerInfos abstractServerInfos = null;
|
||||
|
||||
//根据不同操作系统类型选择不同的数据获取方法
|
||||
if (osName.startsWith("windows")) {
|
||||
abstractServerInfos = new WindowsServerInfos();
|
||||
} else if (osName.startsWith("linux")) {
|
||||
abstractServerInfos = new LinuxServerInfos();
|
||||
} else {//其他服务器类型
|
||||
abstractServerInfos = new LinuxServerInfos();
|
||||
}
|
||||
return abstractServerInfos.getServerInfos();
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成证书2
|
||||
*
|
||||
* @param param 生成证书需要的参数,如:{"subject":"ccx-models","privateAlias":"privateKey","keyPass":"5T7Zz5Y0dJFcqTxvzkH5LDGJJSGMzQ","storePass":"3538cef8e7","licensePath":"C:/Users/zifangsky/Desktop/license.lic","privateKeysStorePath":"C:/Users/zifangsky/Desktop/privateKeys.keystore","issuedTime":"2018-04-26 14:48:12","expiryTime":"2018-12-31 00:00:00","consumerType":"User","consumerAmount":1,"description":"这是证书描述信息","licenseCheckModel":{"ipAddress":["192.168.245.1","10.0.5.22"],"macAddress":["00-50-56-C0-00-01","50-7B-9D-F9-18-41"],"cpuSerial":"BFEBFBFF000406E3","mainBoardSerial":"L1HF65E00X9"}}
|
||||
* @return java.util.Map<java.lang.String, java.lang.Object>
|
||||
*/
|
||||
@RequestMapping(value = "/generateLicense2", produces = {MediaType.APPLICATION_JSON_UTF8_VALUE})
|
||||
public Map<String, Object> generateLicense2(@RequestBody LicenseCreatorParam param) {
|
||||
Map<String, Object> resultMap = new HashMap<>(2);
|
||||
|
||||
if (StringUtils.isBlank(param.getLicensePath())) {
|
||||
param.setLicensePath(licensePath);
|
||||
}
|
||||
|
||||
LicenseCreator licenseCreator = new LicenseCreator(param);
|
||||
boolean result = licenseCreator.generateLicense();
|
||||
|
||||
if (result) {
|
||||
resultMap.put("result", "ok");
|
||||
resultMap.put("msg", param);
|
||||
} else {
|
||||
resultMap.put("result", "error");
|
||||
resultMap.put("msg", "证书文件生成失败!");
|
||||
}
|
||||
return resultMap;
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成证书
|
||||
*
|
||||
* @param param 生成证书需要的参数
|
||||
* @return java.util.Map<java.lang.String, java.lang.Object>
|
||||
*/
|
||||
@RequestMapping(value = "/generateLicense", produces = {MediaType.APPLICATION_JSON_UTF8_VALUE})
|
||||
public Map<String, Object> generateLicense(@RequestBody LicenseGenParam genParam) {
|
||||
Map<String, Object> resultMap = new HashMap<>(2);
|
||||
|
||||
LicenseCreatorParam param = new LicenseCreatorParam();
|
||||
param.setSubject("license_wfc");
|
||||
param.setPrivateAlias("privateKey");
|
||||
param.setKeyPass("wanfi_wfc_Admin123lic");
|
||||
param.setStorePass("public_wfc_Admin123");
|
||||
if (StringUtils.isBlank(genParam.getLicensePath())) {
|
||||
param.setLicensePath(licensePath);
|
||||
} else {
|
||||
param.setLicensePath(genParam.getLicensePath());
|
||||
}
|
||||
// 相对路径resources资源目录
|
||||
String resourcePath = getClass().getClassLoader().getResource("").getPath();
|
||||
param.setPrivateKeysStorePath(resourcePath + "privateKeys.keystore");
|
||||
// param.setPrivateKeysStorePath("D:\\projects\\testdir\\spring-boot2-license\\cloud-license-serve\\target\\classes\\privateCerts.keystore");
|
||||
param.setIssuedTime(new Date());
|
||||
param.setExpiryTime(genParam.getExpiryTime());
|
||||
param.setConsumerType("user");
|
||||
param.setConsumerAmount(1);
|
||||
param.setDescription("这是证书描述信息");
|
||||
LicenseCheckModel licenseCheckModel = new LicenseCheckModel();
|
||||
licenseCheckModel.setIpAddress(genParam.getIpAddress());
|
||||
licenseCheckModel.setMacAddress(genParam.getMacAddress());
|
||||
licenseCheckModel.setCpuSerial(genParam.getCpuSerial());
|
||||
licenseCheckModel.setMainBoardSerial(genParam.getMainBoardSerial());
|
||||
param.setLicenseCheckModel(licenseCheckModel);
|
||||
|
||||
LicenseCreator licenseCreator = new LicenseCreator(param);
|
||||
boolean result = licenseCreator.generateLicense();
|
||||
|
||||
if (result) {
|
||||
resultMap.put("result", "ok");
|
||||
resultMap.put("msg", genParam);
|
||||
} else {
|
||||
resultMap.put("result", "error");
|
||||
resultMap.put("msg", "证书文件生成失败!");
|
||||
}
|
||||
return resultMap;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
package org.wfc.common.license.domain;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 自定义需要校验的License参数
|
||||
*/
|
||||
public class LicenseCheckModel implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 8600137500316662317L;
|
||||
/**
|
||||
* 可被允许的IP地址
|
||||
*/
|
||||
private List<String> ipAddress;
|
||||
|
||||
/**
|
||||
* 可被允许的MAC地址
|
||||
*/
|
||||
private List<String> macAddress;
|
||||
|
||||
/**
|
||||
* 可被允许的CPU序列号
|
||||
*/
|
||||
private String cpuSerial;
|
||||
|
||||
/**
|
||||
* 可被允许的主板序列号
|
||||
*/
|
||||
private String mainBoardSerial;
|
||||
|
||||
public List<String> getIpAddress() {
|
||||
return ipAddress;
|
||||
}
|
||||
|
||||
public void setIpAddress(List<String> ipAddress) {
|
||||
this.ipAddress = ipAddress;
|
||||
}
|
||||
|
||||
public List<String> getMacAddress() {
|
||||
return macAddress;
|
||||
}
|
||||
|
||||
public void setMacAddress(List<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;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "LicenseCheckModel{" +
|
||||
"ipAddress=" + ipAddress +
|
||||
", macAddress=" + macAddress +
|
||||
", cpuSerial='" + cpuSerial + '\'' +
|
||||
", mainBoardSerial='" + mainBoardSerial + '\'' +
|
||||
'}';
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,189 @@
|
||||
package org.wfc.common.license.domain;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* License生成类需要的参数
|
||||
*/
|
||||
public class LicenseCreatorParam implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = -7793154252684580872L;
|
||||
/**
|
||||
* 证书subject
|
||||
*/
|
||||
private String subject;
|
||||
|
||||
/**
|
||||
* 密钥别称
|
||||
*/
|
||||
private String privateAlias;
|
||||
|
||||
/**
|
||||
* 密钥密码(需要妥善保管,不能让使用者知道)
|
||||
*/
|
||||
private String keyPass;
|
||||
|
||||
/**
|
||||
* 访问秘钥库的密码
|
||||
*/
|
||||
private String storePass;
|
||||
|
||||
/**
|
||||
* 证书生成路径
|
||||
*/
|
||||
private String licensePath;
|
||||
|
||||
/**
|
||||
* 密钥库存储路径
|
||||
*/
|
||||
private String privateKeysStorePath;
|
||||
|
||||
/**
|
||||
* 证书生效时间
|
||||
*/
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
|
||||
private Date issuedTime = new Date();
|
||||
|
||||
/**
|
||||
* 证书失效时间
|
||||
*/
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
|
||||
private Date expiryTime;
|
||||
|
||||
/**
|
||||
* 用户类型
|
||||
*/
|
||||
private String consumerType = "user";
|
||||
|
||||
/**
|
||||
* 用户数量
|
||||
*/
|
||||
private Integer consumerAmount = 1;
|
||||
|
||||
/**
|
||||
* 描述信息
|
||||
*/
|
||||
private String description = "";
|
||||
|
||||
/**
|
||||
* 额外的服务器硬件校验信息
|
||||
*/
|
||||
private LicenseCheckModel licenseCheckModel;
|
||||
|
||||
public String getSubject() {
|
||||
return subject;
|
||||
}
|
||||
|
||||
public void setSubject(String subject) {
|
||||
this.subject = subject;
|
||||
}
|
||||
|
||||
public String getPrivateAlias() {
|
||||
return privateAlias;
|
||||
}
|
||||
|
||||
public void setPrivateAlias(String privateAlias) {
|
||||
this.privateAlias = privateAlias;
|
||||
}
|
||||
|
||||
public String getKeyPass() {
|
||||
return keyPass;
|
||||
}
|
||||
|
||||
public void setKeyPass(String keyPass) {
|
||||
this.keyPass = keyPass;
|
||||
}
|
||||
|
||||
public String getStorePass() {
|
||||
return storePass;
|
||||
}
|
||||
|
||||
public void setStorePass(String storePass) {
|
||||
this.storePass = storePass;
|
||||
}
|
||||
|
||||
public String getLicensePath() {
|
||||
return licensePath;
|
||||
}
|
||||
|
||||
public void setLicensePath(String licensePath) {
|
||||
this.licensePath = licensePath;
|
||||
}
|
||||
|
||||
public String getPrivateKeysStorePath() {
|
||||
return privateKeysStorePath;
|
||||
}
|
||||
|
||||
public void setPrivateKeysStorePath(String privateKeysStorePath) {
|
||||
this.privateKeysStorePath = privateKeysStorePath;
|
||||
}
|
||||
|
||||
public Date getIssuedTime() {
|
||||
return issuedTime;
|
||||
}
|
||||
|
||||
public void setIssuedTime(Date issuedTime) {
|
||||
this.issuedTime = issuedTime;
|
||||
}
|
||||
|
||||
public Date getExpiryTime() {
|
||||
return expiryTime;
|
||||
}
|
||||
|
||||
public void setExpiryTime(Date expiryTime) {
|
||||
this.expiryTime = expiryTime;
|
||||
}
|
||||
|
||||
public String getConsumerType() {
|
||||
return consumerType;
|
||||
}
|
||||
|
||||
public void setConsumerType(String consumerType) {
|
||||
this.consumerType = consumerType;
|
||||
}
|
||||
|
||||
public Integer getConsumerAmount() {
|
||||
return consumerAmount;
|
||||
}
|
||||
|
||||
public void setConsumerAmount(Integer consumerAmount) {
|
||||
this.consumerAmount = consumerAmount;
|
||||
}
|
||||
|
||||
public String getDescription() {
|
||||
return description;
|
||||
}
|
||||
|
||||
public void setDescription(String description) {
|
||||
this.description = description;
|
||||
}
|
||||
|
||||
public LicenseCheckModel getLicenseCheckModel() {
|
||||
return licenseCheckModel;
|
||||
}
|
||||
|
||||
public void setLicenseCheckModel(LicenseCheckModel licenseCheckModel) {
|
||||
this.licenseCheckModel = licenseCheckModel;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "LicenseCreatorParam{" +
|
||||
"subject='" + subject + '\'' +
|
||||
", privateAlias='" + privateAlias + '\'' +
|
||||
", keyPass='" + keyPass + '\'' +
|
||||
", storePass='" + storePass + '\'' +
|
||||
", licensePath='" + licensePath + '\'' +
|
||||
", privateKeysStorePath='" + privateKeysStorePath + '\'' +
|
||||
", issuedTime=" + issuedTime +
|
||||
", expiryTime=" + expiryTime +
|
||||
", consumerType='" + consumerType + '\'' +
|
||||
", consumerAmount=" + consumerAmount +
|
||||
", description='" + description + '\'' +
|
||||
", licenseCheckModel=" + licenseCheckModel +
|
||||
'}';
|
||||
}
|
||||
}
|
||||
107
src/main/java/org/wfc/common/license/domain/LicenseGenParam.java
Normal file
107
src/main/java/org/wfc/common/license/domain/LicenseGenParam.java
Normal file
@@ -0,0 +1,107 @@
|
||||
package org.wfc.common.license.domain;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* License生成类需要的参数
|
||||
*/
|
||||
public class LicenseGenParam implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = -7793154252684580871L;
|
||||
|
||||
/**
|
||||
* 证书生成路径
|
||||
*/
|
||||
private String licensePath;
|
||||
|
||||
/**
|
||||
* 证书失效时间
|
||||
*/
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
|
||||
private Date expiryTime;
|
||||
|
||||
/**
|
||||
* 额外的服务器硬件校验信息
|
||||
*/
|
||||
private LicenseCheckModel licenseCheckModel;
|
||||
|
||||
/**
|
||||
* 可被允许的IP地址
|
||||
*/
|
||||
private List<String> ipAddress;
|
||||
|
||||
/**
|
||||
* 可被允许的MAC地址
|
||||
*/
|
||||
private List<String> macAddress;
|
||||
|
||||
/**
|
||||
* 可被允许的CPU序列号
|
||||
*/
|
||||
private String cpuSerial;
|
||||
|
||||
/**
|
||||
* 可被允许的主板序列号
|
||||
*/
|
||||
private String mainBoardSerial;
|
||||
|
||||
public String getLicensePath() {
|
||||
return licensePath;
|
||||
}
|
||||
|
||||
public void setLicensePath(String licensePath) {
|
||||
this.licensePath = licensePath;
|
||||
}
|
||||
|
||||
public Date getExpiryTime() {
|
||||
return expiryTime;
|
||||
}
|
||||
|
||||
public void setExpiryTime(Date expiryTime) {
|
||||
this.expiryTime = expiryTime;
|
||||
}
|
||||
|
||||
public LicenseCheckModel getLicenseCheckModel() {
|
||||
return licenseCheckModel;
|
||||
}
|
||||
|
||||
public void setLicenseCheckModel(LicenseCheckModel licenseCheckModel) {
|
||||
this.licenseCheckModel = licenseCheckModel;
|
||||
}
|
||||
|
||||
public List<String> getIpAddress() {
|
||||
return ipAddress;
|
||||
}
|
||||
|
||||
public void setIpAddress(List<String> ipAddress) {
|
||||
this.ipAddress = ipAddress;
|
||||
}
|
||||
|
||||
public List<String> getMacAddress() {
|
||||
return macAddress;
|
||||
}
|
||||
|
||||
public void setMacAddress(List<String> macAddress) {
|
||||
this.macAddress = macAddress;
|
||||
}
|
||||
|
||||
public String getCpuSerial() {
|
||||
return cpuSerial;
|
||||
}
|
||||
|
||||
public void setCpuSerial(String cpuSerial) {
|
||||
this.cpuSerial = cpuSerial;
|
||||
}
|
||||
|
||||
public String getMainBoardSerial() {
|
||||
return mainBoardSerial;
|
||||
}
|
||||
|
||||
public void setMainBoardSerial(String mainBoardSerial) {
|
||||
this.mainBoardSerial = mainBoardSerial;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,122 @@
|
||||
package org.wfc.common.license.license;
|
||||
|
||||
import org.wfc.common.license.domain.LicenseCheckModel;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import java.net.InetAddress;
|
||||
import java.net.NetworkInterface;
|
||||
import java.net.SocketException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Enumeration;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 用于获取客户服务器的基本信息,如:IP、Mac地址、CPU序列号、主板序列号等
|
||||
*/
|
||||
@Slf4j
|
||||
public abstract class AbstractServerInfos {
|
||||
|
||||
/**
|
||||
* 组装需要额外校验的License参数
|
||||
*
|
||||
* @return demo.LicenseCheckModel
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public LicenseCheckModel getServerInfos() {
|
||||
LicenseCheckModel result = new LicenseCheckModel();
|
||||
|
||||
try {
|
||||
result.setIpAddress(this.getIpAddress());
|
||||
result.setMacAddress(this.getMacAddress());
|
||||
result.setCpuSerial(this.getCPUSerial());
|
||||
result.setMainBoardSerial(this.getMainBoardSerial());
|
||||
} catch (Exception e) {
|
||||
log.error("获取服务器硬件信息失败", e);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取IP地址
|
||||
*
|
||||
* @return java.util.List<java.lang.String>
|
||||
*/
|
||||
protected abstract List<String> getIpAddress() throws Exception;
|
||||
|
||||
/**
|
||||
* 获取Mac地址
|
||||
*
|
||||
* @return java.util.List<java.lang.String>
|
||||
*/
|
||||
protected abstract List<String> getMacAddress() throws Exception;
|
||||
|
||||
/**
|
||||
* 获取CPU序列号
|
||||
*
|
||||
* @return java.lang.String
|
||||
*/
|
||||
protected abstract String getCPUSerial() throws Exception;
|
||||
|
||||
/**
|
||||
* 获取主板序列号
|
||||
*
|
||||
* @return java.lang.String
|
||||
*/
|
||||
protected abstract String getMainBoardSerial() throws Exception;
|
||||
|
||||
/**
|
||||
* 获取当前服务器所有符合条件的InetAddress
|
||||
*
|
||||
* @return java.util.List<java.net.InetAddress>
|
||||
*/
|
||||
protected List<InetAddress> getLocalAllInetAddress() throws Exception {
|
||||
List<InetAddress> result = new ArrayList<>(4);
|
||||
|
||||
// 遍历所有的网络接口
|
||||
for (Enumeration networkInterfaces = NetworkInterface.getNetworkInterfaces(); networkInterfaces.hasMoreElements(); ) {
|
||||
NetworkInterface iface = (NetworkInterface) networkInterfaces.nextElement();
|
||||
// 在所有的接口下再遍历IP
|
||||
for (Enumeration inetAddresses = iface.getInetAddresses(); inetAddresses.hasMoreElements(); ) {
|
||||
InetAddress inetAddr = (InetAddress) inetAddresses.nextElement();
|
||||
|
||||
//排除LoopbackAddress、SiteLocalAddress、LinkLocalAddress、MulticastAddress类型的IP地址
|
||||
if (!inetAddr.isLoopbackAddress() /*&& !inetAddr.isSiteLocalAddress()*/
|
||||
&& !inetAddr.isLinkLocalAddress() && !inetAddr.isMulticastAddress()) {
|
||||
result.add(inetAddr);
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取某个网络接口的Mac地址
|
||||
*
|
||||
* @param
|
||||
* @return void
|
||||
*/
|
||||
protected String getMacByInetAddress(InetAddress inetAddr) {
|
||||
try {
|
||||
byte[] mac = NetworkInterface.getByInetAddress(inetAddr).getHardwareAddress();
|
||||
StringBuffer stringBuffer = new StringBuffer();
|
||||
for (int i = 0; i < mac.length; i++) {
|
||||
if (i != 0) {
|
||||
stringBuffer.append("-");
|
||||
}
|
||||
//将十六进制byte转化为字符串
|
||||
String temp = Integer.toHexString(mac[i] & 0xff);
|
||||
if (temp.length() == 1) {
|
||||
stringBuffer.append("0" + temp);
|
||||
} else {
|
||||
stringBuffer.append(temp);
|
||||
}
|
||||
}
|
||||
return stringBuffer.toString().toUpperCase();
|
||||
} catch (SocketException e) {
|
||||
e.printStackTrace();
|
||||
log.error("获取某个网络接口的Mac地址异常", e.getMessage());
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
package org.wfc.common.license.license;
|
||||
|
||||
import de.schlichtherle.license.AbstractKeyStoreParam;
|
||||
|
||||
import java.io.*;
|
||||
|
||||
/**
|
||||
* 自定义KeyStoreParam,用于将公私钥存储文件存放到其他磁盘位置而不是项目中
|
||||
*/
|
||||
public class CustomKeyStoreParam extends AbstractKeyStoreParam {
|
||||
|
||||
/**
|
||||
* 公钥/私钥在磁盘上的存储路径
|
||||
*/
|
||||
private String storePath;
|
||||
private String alias;
|
||||
private String storePwd;
|
||||
private String keyPwd;
|
||||
|
||||
public CustomKeyStoreParam(Class clazz, String resource, String alias, String storePwd, String keyPwd) {
|
||||
super(clazz, resource);
|
||||
this.storePath = resource;
|
||||
this.alias = alias;
|
||||
this.storePwd = storePwd;
|
||||
this.keyPwd = keyPwd;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public String getAlias() {
|
||||
return alias;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getStorePwd() {
|
||||
return storePwd;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getKeyPwd() {
|
||||
return keyPwd;
|
||||
}
|
||||
|
||||
/**
|
||||
* 复写de.schlichtherle.license.AbstractKeyStoreParam的getStream()方法<br/>
|
||||
* 用于将公私钥存储文件存放到其他磁盘位置而不是项目中
|
||||
*
|
||||
* @param
|
||||
* @return java.io.InputStream
|
||||
*/
|
||||
@Override
|
||||
public InputStream getStream() throws IOException {
|
||||
final InputStream in = new FileInputStream(new File(storePath));
|
||||
if (null == in) {
|
||||
throw new FileNotFoundException(storePath);
|
||||
}
|
||||
|
||||
return in;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,264 @@
|
||||
package org.wfc.common.license.license;
|
||||
|
||||
import org.wfc.common.license.domain.LicenseCheckModel;
|
||||
import de.schlichtherle.license.*;
|
||||
import de.schlichtherle.xml.GenericCertificate;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import java.beans.XMLDecoder;
|
||||
import java.io.BufferedInputStream;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 自定义LicenseManager,用于增加额外的服务器硬件信息校验
|
||||
*/
|
||||
@Slf4j
|
||||
public class CustomLicenseManager extends LicenseManager {
|
||||
|
||||
//XML编码
|
||||
private static final String XML_CHARSET = "UTF-8";
|
||||
//默认BUFSIZE
|
||||
private static final int DEFAULT_BUFSIZE = 8 * 1024;
|
||||
|
||||
public CustomLicenseManager() {
|
||||
|
||||
}
|
||||
|
||||
public CustomLicenseManager(LicenseParam param) {
|
||||
super(param);
|
||||
}
|
||||
|
||||
/**
|
||||
* 复写create方法
|
||||
*
|
||||
* @param
|
||||
* @return byte[]
|
||||
*/
|
||||
@Override
|
||||
protected synchronized byte[] create(
|
||||
LicenseContent content,
|
||||
LicenseNotary notary)
|
||||
throws Exception {
|
||||
initialize(content);
|
||||
this.validateCreate(content);
|
||||
final GenericCertificate certificate = notary.sign(content);
|
||||
return getPrivacyGuard().cert2key(certificate);
|
||||
}
|
||||
|
||||
/**
|
||||
* 复写install方法,其中validate方法调用本类中的validate方法,校验IP地址、Mac地址等其他信息
|
||||
*
|
||||
* @param
|
||||
* @return de.schlichtherle.license.LicenseContent
|
||||
*/
|
||||
@Override
|
||||
protected synchronized LicenseContent install(
|
||||
final byte[] key,
|
||||
final LicenseNotary notary)
|
||||
throws Exception {
|
||||
final GenericCertificate certificate = getPrivacyGuard().key2cert(key);
|
||||
|
||||
notary.verify(certificate);
|
||||
final LicenseContent content = (LicenseContent) this.load(certificate.getEncoded());
|
||||
this.validate(content);
|
||||
setLicenseKey(key);
|
||||
setCertificate(certificate);
|
||||
|
||||
return content;
|
||||
}
|
||||
|
||||
/**
|
||||
* 复写verify方法,调用本类中的validate方法,校验IP地址、Mac地址等其他信息
|
||||
*
|
||||
* @param
|
||||
* @return de.schlichtherle.license.LicenseContent
|
||||
*/
|
||||
@Override
|
||||
protected synchronized LicenseContent verify(final LicenseNotary notary)
|
||||
throws Exception {
|
||||
GenericCertificate certificate = getCertificate();
|
||||
|
||||
// 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) this.load(certificate.getEncoded());
|
||||
this.validate(content);
|
||||
setCertificate(certificate);
|
||||
|
||||
return content;
|
||||
}
|
||||
|
||||
/**
|
||||
* 校验生成证书的参数信息
|
||||
*
|
||||
* @param content 证书正文
|
||||
*/
|
||||
protected synchronized void validateCreate(final LicenseContent content)
|
||||
throws LicenseContentException {
|
||||
final LicenseParam param = getLicenseParam();
|
||||
|
||||
final Date now = new Date();
|
||||
final Date notBefore = content.getNotBefore();
|
||||
final Date notAfter = content.getNotAfter();
|
||||
if (null != notAfter && now.after(notAfter)) {
|
||||
throw new LicenseContentException("证书失效时间不能早于当前时间");
|
||||
}
|
||||
if (null != notBefore && null != notAfter && notAfter.before(notBefore)) {
|
||||
throw new LicenseContentException("证书生效时间不能晚于证书失效时间");
|
||||
}
|
||||
final String consumerType = content.getConsumerType();
|
||||
if (null == consumerType) {
|
||||
throw new LicenseContentException("用户类型不能为空");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 复写validate方法,增加IP地址、Mac地址等其他信息校验
|
||||
*
|
||||
* @param content LicenseContent
|
||||
*/
|
||||
@Override
|
||||
protected synchronized void validate(final LicenseContent content)
|
||||
throws LicenseContentException {
|
||||
//1. 首先调用父类的validate方法
|
||||
super.validate(content);
|
||||
|
||||
//2. 然后校验自定义的License参数
|
||||
//License中可被允许的参数信息
|
||||
LicenseCheckModel expectedCheckModel = (LicenseCheckModel) content.getExtra();
|
||||
//当前服务器真实的参数信息
|
||||
LicenseCheckModel serverCheckModel = getServerInfos();
|
||||
|
||||
if (expectedCheckModel != null && serverCheckModel != null) {
|
||||
//校验IP地址
|
||||
if (!checkIpAddress(expectedCheckModel.getIpAddress(), serverCheckModel.getIpAddress())) {
|
||||
throw new LicenseContentException("当前服务器的IP没在授权范围内");
|
||||
}
|
||||
|
||||
//校验Mac地址
|
||||
if (!checkIpAddress(expectedCheckModel.getMacAddress(), serverCheckModel.getMacAddress())) {
|
||||
throw new LicenseContentException("当前服务器的Mac地址没在授权范围内");
|
||||
}
|
||||
|
||||
//校验主板序列号
|
||||
if (!checkSerial(expectedCheckModel.getMainBoardSerial(), serverCheckModel.getMainBoardSerial())) {
|
||||
throw new LicenseContentException("当前服务器的主板序列号没在授权范围内");
|
||||
}
|
||||
|
||||
//校验CPU序列号
|
||||
if (!checkSerial(expectedCheckModel.getCpuSerial(), serverCheckModel.getCpuSerial())) {
|
||||
throw new LicenseContentException("当前服务器的CPU序列号没在授权范围内");
|
||||
}
|
||||
} else {
|
||||
throw new LicenseContentException("不能获取服务器硬件信息");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 重写XMLDecoder解析XML
|
||||
*
|
||||
* @param encoded XML类型字符串
|
||||
* @return java.lang.Object
|
||||
*/
|
||||
private Object load(String encoded) {
|
||||
BufferedInputStream inputStream = null;
|
||||
XMLDecoder decoder = null;
|
||||
try {
|
||||
inputStream = new BufferedInputStream(new ByteArrayInputStream(encoded.getBytes(XML_CHARSET)));
|
||||
|
||||
decoder = new XMLDecoder(new BufferedInputStream(inputStream, DEFAULT_BUFSIZE), null, null);
|
||||
|
||||
return decoder.readObject();
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
e.printStackTrace();
|
||||
} finally {
|
||||
try {
|
||||
if (decoder != null) {
|
||||
decoder.close();
|
||||
}
|
||||
if (inputStream != null) {
|
||||
inputStream.close();
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.error("XMLDecoder解析XML失败", e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前服务器需要额外校验的License参数
|
||||
*
|
||||
* @return demo.LicenseCheckModel
|
||||
*/
|
||||
private LicenseCheckModel getServerInfos() {
|
||||
//操作系统类型
|
||||
String osName = System.getProperty("os.name").toLowerCase();
|
||||
AbstractServerInfos abstractServerInfos = null;
|
||||
|
||||
//根据不同操作系统类型选择不同的数据获取方法
|
||||
if (osName.startsWith("windows")) {
|
||||
abstractServerInfos = new WindowsServerInfos();
|
||||
} else if (osName.startsWith("linux")) {
|
||||
abstractServerInfos = new LinuxServerInfos();
|
||||
} else {//其他服务器类型
|
||||
abstractServerInfos = new LinuxServerInfos();
|
||||
}
|
||||
|
||||
return abstractServerInfos.getServerInfos();
|
||||
}
|
||||
|
||||
/**
|
||||
* 校验当前服务器的IP/Mac地址是否在可被允许的IP范围内<br/>
|
||||
* 如果存在IP在可被允许的IP/Mac地址范围内,则返回true
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
private boolean checkIpAddress(List<String> expectedList, List<String> serverList) {
|
||||
if (expectedList != null && expectedList.size() > 0) {
|
||||
if (serverList != null && serverList.size() > 0) {
|
||||
for (String expected : expectedList) {
|
||||
if (serverList.contains(expected.trim())) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 校验当前服务器硬件(主板、CPU等)序列号是否在可允许范围内
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
private boolean checkSerial(String expectedSerial, String serverSerial) {
|
||||
if (StringUtils.isNotBlank(expectedSerial)) {
|
||||
if (StringUtils.isNotBlank(serverSerial)) {
|
||||
if (expectedSerial.equals(serverSerial)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,93 @@
|
||||
package org.wfc.common.license.license;
|
||||
|
||||
import org.wfc.common.license.domain.LicenseCreatorParam;
|
||||
import de.schlichtherle.license.*;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import javax.security.auth.x500.X500Principal;
|
||||
import java.io.File;
|
||||
import java.text.MessageFormat;
|
||||
import java.util.prefs.Preferences;
|
||||
|
||||
/**
|
||||
* License生成类
|
||||
*/
|
||||
@Slf4j
|
||||
public class LicenseCreator {
|
||||
|
||||
private final static X500Principal DEFAULT_HOLDER_AND_ISSUER = new X500Principal("CN=localhost, OU=localhost, O=localhost, L=SH, ST=SH, C=CN");
|
||||
private LicenseCreatorParam param;
|
||||
|
||||
public LicenseCreator(LicenseCreatorParam param) {
|
||||
this.param = param;
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成License证书
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public boolean generateLicense() {
|
||||
try {
|
||||
LicenseManager licenseManager = new CustomLicenseManager(initLicenseParam());
|
||||
LicenseContent licenseContent = initLicenseContent();
|
||||
|
||||
licenseManager.store(licenseContent, new File(param.getLicensePath()));
|
||||
|
||||
return true;
|
||||
} catch (Exception e) {
|
||||
log.error(MessageFormat.format("证书生成失败:{0}", param), e.getMessage(), e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化证书生成参数
|
||||
*
|
||||
* @return de.schlichtherle.license.LicenseParam
|
||||
*/
|
||||
private LicenseParam initLicenseParam() {
|
||||
Preferences preferences = Preferences.userNodeForPackage(LicenseCreator.class);
|
||||
|
||||
//设置对证书内容加密的秘钥
|
||||
CipherParam cipherParam = new DefaultCipherParam(param.getStorePass());
|
||||
|
||||
KeyStoreParam privateStoreParam = new CustomKeyStoreParam(LicenseCreator.class
|
||||
, param.getPrivateKeysStorePath()
|
||||
, param.getPrivateAlias()
|
||||
, param.getStorePass()
|
||||
, param.getKeyPass());
|
||||
|
||||
LicenseParam licenseParam = new DefaultLicenseParam(param.getSubject()
|
||||
, preferences
|
||||
, privateStoreParam
|
||||
, cipherParam);
|
||||
|
||||
return licenseParam;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置证书生成正文信息
|
||||
*
|
||||
* @return de.schlichtherle.license.LicenseContent
|
||||
*/
|
||||
private LicenseContent initLicenseContent() {
|
||||
LicenseContent licenseContent = new LicenseContent();
|
||||
licenseContent.setHolder(DEFAULT_HOLDER_AND_ISSUER);
|
||||
licenseContent.setIssuer(DEFAULT_HOLDER_AND_ISSUER);
|
||||
|
||||
licenseContent.setSubject(param.getSubject());
|
||||
licenseContent.setIssued(param.getIssuedTime());
|
||||
licenseContent.setNotBefore(param.getIssuedTime());
|
||||
licenseContent.setNotAfter(param.getExpiryTime());
|
||||
licenseContent.setConsumerType(param.getConsumerType());
|
||||
licenseContent.setConsumerAmount(param.getConsumerAmount());
|
||||
licenseContent.setInfo(param.getDescription());
|
||||
|
||||
//扩展校验服务器硬件信息
|
||||
licenseContent.setExtra(param.getLicenseCheckModel());
|
||||
|
||||
return licenseContent;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,86 @@
|
||||
package org.wfc.common.license.license;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.InputStreamReader;
|
||||
import java.net.InetAddress;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* 用于获取客户Linux服务器的基本信息
|
||||
*/
|
||||
public class LinuxServerInfos extends AbstractServerInfos {
|
||||
|
||||
@Override
|
||||
protected List<String> getIpAddress() throws Exception {
|
||||
List<String> result = null;
|
||||
|
||||
//获取所有网络接口
|
||||
List<InetAddress> inetAddresses = getLocalAllInetAddress();
|
||||
|
||||
if (inetAddresses != null && inetAddresses.size() > 0) {
|
||||
result = inetAddresses.stream().map(InetAddress::getHostAddress).distinct().map(String::toLowerCase).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<String> getMacAddress() throws Exception {
|
||||
List<String> result = null;
|
||||
|
||||
//1. 获取所有网络接口
|
||||
List<InetAddress> inetAddresses = getLocalAllInetAddress();
|
||||
|
||||
if (inetAddresses != null && inetAddresses.size() > 0) {
|
||||
//2. 获取所有网络接口的Mac地址
|
||||
result = inetAddresses.stream().map(this::getMacByInetAddress).distinct().collect(Collectors.toList());
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getCPUSerial() throws Exception {
|
||||
//序列号
|
||||
String serialNumber = "";
|
||||
|
||||
//使用dmidecode命令获取CPU序列号
|
||||
String[] shell = {"/bin/bash", "-c", "dmidecode -t processor | grep 'ID' | awk -F ':' '{print $2}' | head -n 1"};
|
||||
Process process = Runtime.getRuntime().exec(shell);
|
||||
process.getOutputStream().close();
|
||||
|
||||
BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
|
||||
|
||||
String line = reader.readLine().trim();
|
||||
if (StringUtils.isNotBlank(line)) {
|
||||
serialNumber = line;
|
||||
}
|
||||
|
||||
reader.close();
|
||||
return serialNumber;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getMainBoardSerial() throws Exception {
|
||||
//序列号
|
||||
String serialNumber = "";
|
||||
|
||||
//使用dmidecode命令获取主板序列号
|
||||
String[] shell = {"/bin/bash", "-c", "dmidecode | grep 'Serial Number' | awk -F ':' '{print $2}' | head -n 1"};
|
||||
Process process = Runtime.getRuntime().exec(shell);
|
||||
process.getOutputStream().close();
|
||||
|
||||
BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
|
||||
|
||||
String line = reader.readLine().trim();
|
||||
if (StringUtils.isNotBlank(line)) {
|
||||
serialNumber = line;
|
||||
}
|
||||
|
||||
reader.close();
|
||||
return serialNumber;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,144 @@
|
||||
package org.wfc.common.license.license;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.File;
|
||||
import java.io.FileWriter;
|
||||
import java.io.InputStreamReader;
|
||||
import java.net.InetAddress;
|
||||
import java.util.List;
|
||||
import java.util.Scanner;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* 用于获取客户Windows服务器的基本信息
|
||||
*/
|
||||
public class WindowsServerInfos extends AbstractServerInfos {
|
||||
|
||||
@Override
|
||||
protected List<String> getIpAddress() throws Exception {
|
||||
List<String> result = null;
|
||||
|
||||
//获取所有网络接口
|
||||
List<InetAddress> inetAddresses = getLocalAllInetAddress();
|
||||
|
||||
if (inetAddresses != null && inetAddresses.size() > 0) {
|
||||
result = inetAddresses.stream().map(InetAddress::getHostAddress).distinct().map(String::toLowerCase).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<String> getMacAddress() throws Exception {
|
||||
List<String> result = null;
|
||||
|
||||
//1. 获取所有网络接口
|
||||
List<InetAddress> inetAddresses = getLocalAllInetAddress();
|
||||
|
||||
if (inetAddresses != null && inetAddresses.size() > 0) {
|
||||
//2. 获取所有网络接口的Mac地址
|
||||
result = inetAddresses.stream().map(this::getMacByInetAddress).distinct().collect(Collectors.toList());
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private String getCPUSerial2() throws Exception {
|
||||
//序列号
|
||||
String serialNumber = "";
|
||||
|
||||
//使用WMIC获取CPU序列号
|
||||
Process process = Runtime.getRuntime().exec("wmic cpu get processorid");
|
||||
process.getOutputStream().close();
|
||||
Scanner scanner = new Scanner(process.getInputStream());
|
||||
|
||||
if (scanner != null && scanner.hasNext()) {
|
||||
scanner.next();
|
||||
}
|
||||
|
||||
if (scanner.hasNext()) {
|
||||
serialNumber = scanner.next().trim();
|
||||
}
|
||||
|
||||
scanner.close();
|
||||
return serialNumber;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getCPUSerial() throws Exception {
|
||||
String result = "";
|
||||
try {
|
||||
File 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());
|
||||
BufferedReader input = new BufferedReader(new InputStreamReader(p.getInputStream()));
|
||||
String line;
|
||||
while ((line = input.readLine()) != null) {
|
||||
result += line;
|
||||
}
|
||||
input.close();
|
||||
file.delete();
|
||||
} catch (Exception e) {
|
||||
return getCPUSerial2();
|
||||
}
|
||||
return result.trim();
|
||||
}
|
||||
|
||||
private String getMainBoardSerial2() throws Exception {
|
||||
//序列号
|
||||
String serialNumber = "";
|
||||
|
||||
//使用WMIC获取主板序列号
|
||||
Process process = Runtime.getRuntime().exec("wmic baseboard get serialnumber");
|
||||
process.getOutputStream().close();
|
||||
Scanner scanner = new Scanner(process.getInputStream());
|
||||
|
||||
if (scanner != null && scanner.hasNext()) {
|
||||
scanner.next();
|
||||
}
|
||||
|
||||
if (scanner.hasNext()) {
|
||||
serialNumber = scanner.next().trim();
|
||||
}
|
||||
|
||||
scanner.close();
|
||||
return serialNumber;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getMainBoardSerial() throws Exception {
|
||||
|
||||
String result = "";
|
||||
try {
|
||||
File 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());
|
||||
BufferedReader input = new BufferedReader(new InputStreamReader(p.getInputStream()));
|
||||
String line;
|
||||
while ((line = input.readLine()) != null) {
|
||||
result += line;
|
||||
}
|
||||
input.close();
|
||||
} catch (Exception e) {
|
||||
return getMainBoardSerial2();
|
||||
}
|
||||
return result.trim();
|
||||
}
|
||||
}
|
||||
10
src/main/resources/application.properties
Normal file
10
src/main/resources/application.properties
Normal file
@@ -0,0 +1,10 @@
|
||||
server.port=8081
|
||||
|
||||
#<23><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Źػ<C5B9>
|
||||
server.shutdown=graceful
|
||||
#<23><><EFBFBD><EFBFBD>10<31><30>
|
||||
spring.lifecycle.timeout-per-shutdown-phase=10s
|
||||
|
||||
#License<73><65><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
||||
license.licensePath=/opt/license/license.lic
|
||||
|
||||
23
src/main/resources/banner.txt
Normal file
23
src/main/resources/banner.txt
Normal file
@@ -0,0 +1,23 @@
|
||||
${AnsiColor.BRIGHT_YELLOW}Spring Boot: ${spring-boot.formatted-version}
|
||||
${AnsiColor.BRIGHT_GREEN}$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$
|
||||
${AnsiColor.BRIGHT_YELLOW}$$ _.ooOoo._ $$
|
||||
${AnsiColor.BRIGHT_RED}$$ o888888888o $$
|
||||
${AnsiColor.BRIGHT_CYAN}$$ 88" . "88 $$
|
||||
${AnsiColor.BRIGHT_MAGENTA}$$ (| ^_^ |) $$
|
||||
${AnsiColor.BRIGHT_GREEN}$$ O\ = /O $$
|
||||
${AnsiColor.BRIGHT_RED}$$ ____/`-----'\____ $$
|
||||
${AnsiColor.BRIGHT_CYAN}$$ .' \\| |$$ `. $$
|
||||
${AnsiColor.BRIGHT_MAGENTA}$$ / \\||| : |||$$ \ $$
|
||||
${AnsiColor.BRIGHT_GREEN}$$ / _||||| -:- |||||- \ $$
|
||||
${AnsiColor.BRIGHT_YELLOW}$$ | | \\\ - $$/ | | $$
|
||||
${AnsiColor.BRIGHT_GREEN}$$ | \_| ''\-----/'' | | $$
|
||||
${AnsiColor.BRIGHT_YELLOW}$$ \ .-\___ `-` ____/-. / $$
|
||||
${AnsiColor.BRIGHT_CYAN}$$ ___`. .' /--.--\ `. . ___ $$
|
||||
${AnsiColor.BRIGHT_RED}$$ ."" '< `.____\_<|>_/____.' >'"". $$
|
||||
${AnsiColor.BRIGHT_GREEN}$$ | | : `- \`.;`.\ _ /``;.`/ - ` : | | $$
|
||||
${AnsiColor.BRIGHT_YELLOW}$$ \ \ `-. \_ ___\ /___ _/ .-` / / $$
|
||||
${AnsiColor.BRIGHT_CYAN}$$ ========`-.____`-.____\_____/____.-`____.-'======== $$
|
||||
${AnsiColor.BRIGHT_MAGENTA}$$ `=---=' $$
|
||||
${AnsiColor.BRIGHT_YELLOW}$$ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ $$
|
||||
${AnsiColor.BRIGHT_GREEN}$$ 佛祖保佑 永无BUG 永不修改 $$
|
||||
${AnsiColor.BRIGHT_YELLOW}$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$
|
||||
BIN
src/main/resources/privateKeys.keystore
Normal file
BIN
src/main/resources/privateKeys.keystore
Normal file
Binary file not shown.
Reference in New Issue
Block a user