证书认证
parent
4c136402bd
commit
c478a70bef
@ -0,0 +1,43 @@
|
||||
package cn.iocoder.yudao.server.license;
|
||||
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* LicenseCheckInterceptor
|
||||
*
|
||||
* @author zifangsky
|
||||
* @date 2018/4/25
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public class LicenseCheckInterceptor extends HandlerInterceptorAdapter{
|
||||
private static Logger logger = LogManager.getLogger(LicenseCheckInterceptor.class);
|
||||
|
||||
@Override
|
||||
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
|
||||
|
||||
LicenseVerify licenseVerify = new LicenseVerify();
|
||||
|
||||
//校验证书是否有效
|
||||
boolean verifyResult = licenseVerify.verify();
|
||||
|
||||
if(verifyResult){
|
||||
return true;
|
||||
}else{
|
||||
Map<String,String> result = new HashMap<>(1);
|
||||
result.put("result","您的证书无效,请核查服务器是否取得授权或重新申请证书!");
|
||||
response.setContentType("text/html;charset=UTF-8");
|
||||
response.getWriter().write(JSON.toJSONString(result));
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,76 @@
|
||||
package cn.iocoder.yudao.server.license;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.ApplicationListener;
|
||||
import org.springframework.context.event.ContextRefreshedEvent;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* 在项目启动时安装证书
|
||||
*
|
||||
* @author zifangsky
|
||||
* @date 2018/4/24
|
||||
* @since 1.0.0
|
||||
*/
|
||||
@Component
|
||||
public class LicenseCheckListener implements ApplicationListener<ContextRefreshedEvent> {
|
||||
private static Logger logger = LogManager.getLogger(LicenseCheckListener.class);
|
||||
|
||||
/**
|
||||
* 证书subject
|
||||
*/
|
||||
@Value("${license.subject}")
|
||||
private String subject;
|
||||
|
||||
/**
|
||||
* 公钥别称
|
||||
*/
|
||||
@Value("${license.publicAlias}")
|
||||
private String publicAlias;
|
||||
|
||||
/**
|
||||
* 访问公钥库的密码
|
||||
*/
|
||||
@Value("${license.storePass}")
|
||||
private String storePass;
|
||||
|
||||
/**
|
||||
* 证书生成路径
|
||||
*/
|
||||
@Value("${license.licensePath}")
|
||||
private String licensePath;
|
||||
|
||||
/**
|
||||
* 密钥库存储路径
|
||||
*/
|
||||
@Value("${license.publicKeysStorePath}")
|
||||
private String publicKeysStorePath;
|
||||
|
||||
@Override
|
||||
public void onApplicationEvent(ContextRefreshedEvent event) {
|
||||
//root application context 没有parent
|
||||
ApplicationContext context = event.getApplicationContext().getParent();
|
||||
if(context == null){
|
||||
if(StringUtils.isNotBlank(licensePath)){
|
||||
logger.info("++++++++ 开始安装证书 ++++++++");
|
||||
|
||||
LicenseVerifyParam param = new LicenseVerifyParam();
|
||||
param.setSubject(subject);
|
||||
param.setPublicAlias(publicAlias);
|
||||
param.setStorePass(storePass);
|
||||
param.setLicensePath(licensePath);
|
||||
param.setPublicKeysStorePath(publicKeysStorePath);
|
||||
|
||||
LicenseVerify licenseVerify = new LicenseVerify();
|
||||
//安装证书
|
||||
licenseVerify.install(param);
|
||||
|
||||
logger.info("++++++++ 证书安装结束 ++++++++");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,40 @@
|
||||
package cn.iocoder.yudao.server.license;
|
||||
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 自定义需要校验的License参数
|
||||
*
|
||||
* @author zifangsky
|
||||
* @date 2018/4/23
|
||||
* @since 1.0.0
|
||||
*/
|
||||
@Data
|
||||
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;
|
||||
|
||||
}
|
@ -0,0 +1,29 @@
|
||||
package cn.iocoder.yudao.server.license;
|
||||
|
||||
import de.schlichtherle.license.LicenseManager;
|
||||
import de.schlichtherle.license.LicenseParam;
|
||||
|
||||
/**
|
||||
* de.schlichtherle.license.LicenseManager的单例
|
||||
*
|
||||
* @author zifangsky
|
||||
* @date 2018/4/19
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public class LicenseManagerHolder {
|
||||
|
||||
private static volatile LicenseManager LICENSE_MANAGER;
|
||||
|
||||
public static LicenseManager getInstance(LicenseParam param){
|
||||
if(LICENSE_MANAGER == null){
|
||||
synchronized (LicenseManagerHolder.class){
|
||||
if(LICENSE_MANAGER == null){
|
||||
LICENSE_MANAGER = new CustomLicenseManager(param);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return LICENSE_MANAGER;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,96 @@
|
||||
package cn.iocoder.yudao.server.license;
|
||||
|
||||
import de.schlichtherle.license.*;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
import java.io.File;
|
||||
import java.text.DateFormat;
|
||||
import java.text.MessageFormat;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.prefs.Preferences;
|
||||
|
||||
/**
|
||||
* License校验类
|
||||
*
|
||||
* @author zifangsky
|
||||
* @date 2018/4/20
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public class LicenseVerify {
|
||||
private static Logger logger = LogManager.getLogger(LicenseVerify.class);
|
||||
|
||||
/**
|
||||
* 安装License证书
|
||||
* @author zifangsky
|
||||
* @date 2018/4/20 16:26
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public synchronized LicenseContent install(LicenseVerifyParam param){
|
||||
LicenseContent result = null;
|
||||
DateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
|
||||
|
||||
//1. 安装证书
|
||||
try{
|
||||
LicenseManager licenseManager = LicenseManagerHolder.getInstance(initLicenseParam(param));
|
||||
licenseManager.uninstall();
|
||||
|
||||
result = licenseManager.install(new File(param.getLicensePath()));
|
||||
logger.info(MessageFormat.format("证书安装成功,证书有效期:{0} - {1}",format.format(result.getNotBefore()),format.format(result.getNotAfter())));
|
||||
}catch (Exception e){
|
||||
logger.error("证书安装失败!",e);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 校验License证书
|
||||
* @author zifangsky
|
||||
* @date 2018/4/20 16:26
|
||||
* @since 1.0.0
|
||||
* @return boolean
|
||||
*/
|
||||
public boolean verify(){
|
||||
LicenseManager licenseManager = LicenseManagerHolder.getInstance(null);
|
||||
DateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
|
||||
|
||||
//2. 校验证书
|
||||
try {
|
||||
LicenseContent licenseContent = licenseManager.verify();
|
||||
// System.out.println(licenseContent.getSubject());
|
||||
|
||||
logger.info(MessageFormat.format("证书校验通过,证书有效期:{0} - {1}",format.format(licenseContent.getNotBefore()),format.format(licenseContent.getNotAfter())));
|
||||
return true;
|
||||
}catch (Exception e){
|
||||
logger.error("证书校验失败!",e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化证书生成参数
|
||||
* @author zifangsky
|
||||
* @date 2018/4/20 10:56
|
||||
* @since 1.0.0
|
||||
* @param param License校验类需要的参数
|
||||
* @return de.schlichtherle.license.LicenseParam
|
||||
*/
|
||||
private LicenseParam initLicenseParam(LicenseVerifyParam param){
|
||||
Preferences preferences = Preferences.userNodeForPackage(LicenseVerify.class);
|
||||
|
||||
CipherParam cipherParam = new DefaultCipherParam(param.getStorePass());
|
||||
|
||||
KeyStoreParam publicStoreParam = new CustomKeyStoreParam(LicenseVerify.class
|
||||
,param.getPublicKeysStorePath()
|
||||
,param.getPublicAlias()
|
||||
,param.getStorePass()
|
||||
,null);
|
||||
|
||||
return new DefaultLicenseParam(param.getSubject()
|
||||
,preferences
|
||||
,publicStoreParam
|
||||
,cipherParam);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,101 @@
|
||||
package cn.iocoder.yudao.server.license;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* License校验类需要的参数
|
||||
*
|
||||
* @author zifangsky
|
||||
* @date 2018/4/20
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public class LicenseVerifyParam {
|
||||
|
||||
/**
|
||||
* 证书subject
|
||||
*/
|
||||
private String subject;
|
||||
|
||||
/**
|
||||
* 公钥别称
|
||||
*/
|
||||
private String publicAlias;
|
||||
|
||||
/**
|
||||
* 访问公钥库的密码
|
||||
*/
|
||||
private String storePass;
|
||||
|
||||
/**
|
||||
* 证书生成路径
|
||||
*/
|
||||
private String licensePath;
|
||||
|
||||
/**
|
||||
* 密钥库存储路径
|
||||
*/
|
||||
private String publicKeysStorePath;
|
||||
|
||||
public LicenseVerifyParam() {
|
||||
|
||||
}
|
||||
|
||||
public LicenseVerifyParam(String subject, String publicAlias, String storePass, String licensePath, String publicKeysStorePath) {
|
||||
this.subject = subject;
|
||||
this.publicAlias = publicAlias;
|
||||
this.storePass = storePass;
|
||||
this.licensePath = licensePath;
|
||||
this.publicKeysStorePath = publicKeysStorePath;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
public String getLicensePath() {
|
||||
return licensePath;
|
||||
}
|
||||
|
||||
public void setLicensePath(String licensePath) {
|
||||
this.licensePath = licensePath;
|
||||
}
|
||||
|
||||
public String getPublicKeysStorePath() {
|
||||
return publicKeysStorePath;
|
||||
}
|
||||
|
||||
public void setPublicKeysStorePath(String publicKeysStorePath) {
|
||||
this.publicKeysStorePath = publicKeysStorePath;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "LicenseVerifyParam{" +
|
||||
"subject='" + subject + '\'' +
|
||||
", publicAlias='" + publicAlias + '\'' +
|
||||
", storePass='" + storePass + '\'' +
|
||||
", licensePath='" + licensePath + '\'' +
|
||||
", publicKeysStorePath='" + publicKeysStorePath + '\'' +
|
||||
'}';
|
||||
}
|
||||
}
|
@ -0,0 +1,90 @@
|
||||
package cn.iocoder.yudao.server.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服务器的基本信息
|
||||
*
|
||||
* @author zifangsky
|
||||
* @date 2018/4/23
|
||||
* @since 1.0.0
|
||||
*/
|
||||
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,89 @@
|
||||
package cn.iocoder.yudao.server.license;
|
||||
|
||||
import java.net.InetAddress;
|
||||
import java.util.List;
|
||||
import java.util.Scanner;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* 用于获取客户Windows服务器的基本信息
|
||||
*
|
||||
* @author zifangsky
|
||||
* @date 2018/4/23
|
||||
* @since 1.0.0
|
||||
*/
|
||||
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;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getCPUSerial() 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 getMainBoardSerial() 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;
|
||||
}
|
||||
}
|
@ -0,0 +1,35 @@
|
||||
package cn.iocoder.yudao.server.license.config;
|
||||
|
||||
|
||||
import cn.iocoder.yudao.server.license.LicenseCheckInterceptor;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
|
||||
import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
|
||||
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
|
||||
|
||||
/**
|
||||
* Web相关配置
|
||||
* @author zifangsky
|
||||
* @date 2018/7/9
|
||||
* @since 1.0.0
|
||||
*/
|
||||
@Configuration
|
||||
public class WebMvcConfig implements WebMvcConfigurer {
|
||||
|
||||
/**
|
||||
* 视图控制器
|
||||
*/
|
||||
@Override
|
||||
public void addViewControllers(ViewControllerRegistry registry) {
|
||||
registry.addViewController("/index").setViewName("index");
|
||||
registry.addViewController("/login").setViewName("login");
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加拦截器
|
||||
*/
|
||||
@Override
|
||||
public void addInterceptors(InterceptorRegistry registry) {
|
||||
registry.addInterceptor(new LicenseCheckInterceptor()).addPathPatterns("/admin-api/system/auth/login");
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue