项目初始化
This commit is contained in:
30
tools/activate-tool/pom.xml
Normal file
30
tools/activate-tool/pom.xml
Normal file
@@ -0,0 +1,30 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent>
|
||||
<groupId>com.njcn.gather</groupId>
|
||||
<artifactId>tools</artifactId>
|
||||
<version>1.0.0</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>activate-tool</artifactId>
|
||||
|
||||
<properties>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>com.njcn</groupId>
|
||||
<artifactId>njcn-common</artifactId>
|
||||
<version>0.0.1</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.njcn</groupId>
|
||||
<artifactId>spingboot2.3.12</artifactId>
|
||||
<version>2.3.12</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
@@ -0,0 +1,33 @@
|
||||
package com.njcn.gather.tool.active.config;
|
||||
|
||||
import cn.hutool.system.SystemUtil;
|
||||
import lombok.Data;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
@Data
|
||||
@ConfigurationProperties(prefix = "activate")
|
||||
public class ActivateProperties {
|
||||
private final String LICENSE_FILE_NAME = "license.key";
|
||||
|
||||
/**
|
||||
* RSA公钥
|
||||
*/
|
||||
private String publicKey;
|
||||
/**
|
||||
* RSA私钥
|
||||
*/
|
||||
private String privateKey;
|
||||
|
||||
/**
|
||||
* 密钥文件所在目录,默认为当前项目目录
|
||||
*/
|
||||
private String licenseDir = System.getProperty(SystemUtil.USER_DIR);
|
||||
|
||||
public String getLicenseFilePath() {
|
||||
return licenseDir + File.separator + LICENSE_FILE_NAME;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,113 @@
|
||||
package com.njcn.gather.tool.active.controller;
|
||||
|
||||
import cn.hutool.core.net.NetUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.hutool.json.JSONUtil;
|
||||
import com.njcn.common.pojo.annotation.OperateInfo;
|
||||
import com.njcn.common.pojo.enums.common.LogEnum;
|
||||
import com.njcn.common.pojo.enums.response.CommonResponseEnum;
|
||||
import com.njcn.common.pojo.exception.BusinessException;
|
||||
import com.njcn.common.pojo.response.HttpResult;
|
||||
import com.njcn.common.utils.LogUtil;
|
||||
import com.njcn.gather.tool.active.service.ActivateService;
|
||||
import com.njcn.gather.tool.active.vo.ActivationCodePlaintext;
|
||||
import com.njcn.web.controller.BaseController;
|
||||
import com.njcn.web.utils.HttpResultUtil;
|
||||
import io.swagger.annotations.*;
|
||||
import lombok.Data;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
@Slf4j
|
||||
@Api(tags = "设备激活管理")
|
||||
@RestController
|
||||
@RequestMapping("/activate")
|
||||
@RequiredArgsConstructor
|
||||
public class ActivateController extends BaseController {
|
||||
|
||||
private final ActivateService activateService;
|
||||
|
||||
@OperateInfo(info = LogEnum.BUSINESS_COMMON)
|
||||
@ApiOperation("生成设备申请码")
|
||||
@PostMapping("/generateApplicationCode")
|
||||
public HttpResult generateApplicationCode() {
|
||||
String methodDescribe = getMethodDescribe("generateApplicationCode");
|
||||
LogUtil.njcnDebug(log, "{},生成设备申请码", methodDescribe);
|
||||
String applicationCode = activateService.generateApplicationCode();
|
||||
return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, applicationCode, methodDescribe);
|
||||
}
|
||||
|
||||
@OperateInfo(info = LogEnum.BUSINESS_COMMON)
|
||||
@ApiOperation("验证设备激活码")
|
||||
@ApiImplicitParam(name = "params", value = "验证设备激活码参数", required = true)
|
||||
@PostMapping("/verifyActivationCode")
|
||||
public HttpResult verifyActivationCode(@RequestBody VerifyActivationCodeParams params) {
|
||||
String methodDescribe = getMethodDescribe("verifyActivationCode");
|
||||
LogUtil.njcnDebug(log, "{},验证设备激活码\":{}", methodDescribe, JSONUtil.toJsonStr(params));
|
||||
ActivationCodePlaintext activationCodePlaintext = activateService.verifyActivationCode(params.getActivationCode());
|
||||
return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, activationCodePlaintext, methodDescribe);
|
||||
}
|
||||
|
||||
@OperateInfo(info = LogEnum.BUSINESS_COMMON)
|
||||
@ApiOperation("获取许可信息")
|
||||
@PostMapping("/getLicense")
|
||||
public HttpResult getLicense() {
|
||||
String methodDescribe = getMethodDescribe("checkLicense");
|
||||
LogUtil.njcnDebug(log, "{},获取许可信息", methodDescribe);
|
||||
ActivationCodePlaintext activationCodePlaintext = null;
|
||||
try {
|
||||
activationCodePlaintext = activateService.readLicenseFile();
|
||||
} catch (BusinessException e) {
|
||||
methodDescribe = "无效的许可信息";
|
||||
}
|
||||
String macAddress = NetUtil.getLocalMacAddress();
|
||||
if (activationCodePlaintext == null) {
|
||||
activationCodePlaintext = new ActivationCodePlaintext();
|
||||
activationCodePlaintext.setMacAddress(macAddress);
|
||||
activationCodePlaintext.init();
|
||||
} else {
|
||||
// 校验mac地址
|
||||
String licenseMacAddress = activationCodePlaintext.getMacAddress();
|
||||
if (StrUtil.isNotEmpty(licenseMacAddress)) {
|
||||
if (!StrUtil.equals(licenseMacAddress, macAddress)) {
|
||||
log.error("mac地址不匹配,无效的许可文件,本机mac:{},许可mac:{}", macAddress, licenseMacAddress);
|
||||
methodDescribe = "mac地址不匹配,无效的许可文件";
|
||||
activationCodePlaintext = new ActivationCodePlaintext();
|
||||
activationCodePlaintext.setMacAddress(macAddress);
|
||||
activationCodePlaintext.init();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, activationCodePlaintext, methodDescribe);
|
||||
}
|
||||
|
||||
@Data
|
||||
public static class VerifyActivationCodeParams {
|
||||
@ApiModelProperty(value = "激活码")
|
||||
private String activationCode;
|
||||
}
|
||||
|
||||
|
||||
@ApiOperation("生成设备激活码")
|
||||
@ApiImplicitParam(name = "params", value = "参数", required = true, dataType = "ApplicationCodeParams")
|
||||
@PostMapping("/generateActivationCode")
|
||||
public HttpResult generateActivationCode(@RequestBody ApplicationCodeParams params) {
|
||||
String activationCode = activateService.generateActivationCode(params.getApplicationCode(), params.getActivationModule());
|
||||
return HttpResultUtil.assembleResult(CommonResponseEnum.SUCCESS.getCode(), activationCode, "");
|
||||
}
|
||||
|
||||
@ApiModel("生成设备激活码参数")
|
||||
@Data
|
||||
public static class ApplicationCodeParams {
|
||||
@ApiModelProperty(value = "设备申请码", required = true)
|
||||
private String applicationCode;
|
||||
@ApiModelProperty(value = "激活模块", required = true)
|
||||
private ActivationCodePlaintext activationModule;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
package com.njcn.gather.tool.active.service;
|
||||
|
||||
import com.njcn.gather.tool.active.vo.ActivationCodePlaintext;
|
||||
|
||||
public interface ActivateService {
|
||||
|
||||
|
||||
/**
|
||||
* 生成设备申请码
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
String generateApplicationCode();
|
||||
|
||||
/**
|
||||
* 验证激活码
|
||||
*
|
||||
* @param activationCode
|
||||
* @return
|
||||
*/
|
||||
ActivationCodePlaintext verifyActivationCode(String activationCode);
|
||||
|
||||
/**
|
||||
* 读取授权文件
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
ActivationCodePlaintext readLicenseFile();
|
||||
|
||||
/**
|
||||
* 生成设备激活码
|
||||
*
|
||||
* @param applicationCode 申请码
|
||||
* @param activationCodePlaintext
|
||||
* @return
|
||||
*/
|
||||
String generateActivationCode(String applicationCode, ActivationCodePlaintext activationCodePlaintext);
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,164 @@
|
||||
package com.njcn.gather.tool.active.service.impl;
|
||||
|
||||
import cn.hutool.core.io.FileUtil;
|
||||
import cn.hutool.core.net.NetUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.hutool.json.JSONUtil;
|
||||
import com.njcn.common.pojo.enums.response.CommonResponseEnum;
|
||||
import com.njcn.common.pojo.exception.BusinessException;
|
||||
import com.njcn.common.utils.RSAUtil;
|
||||
import com.njcn.gather.tool.active.config.ActivateProperties;
|
||||
import com.njcn.gather.tool.active.service.ActivateService;
|
||||
import com.njcn.gather.tool.active.vo.ActivationCodePlaintext;
|
||||
import com.njcn.gather.tool.active.vo.ActivationModule;
|
||||
import com.njcn.gather.tool.active.vo.ApplicationCodePlaintext;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
@EnableConfigurationProperties(ActivateProperties.class)
|
||||
@RequiredArgsConstructor
|
||||
@Slf4j
|
||||
@Service
|
||||
public class ActivateServiceImpl implements ActivateService {
|
||||
|
||||
private final ActivateProperties props;
|
||||
|
||||
@Override
|
||||
public String generateApplicationCode() {
|
||||
// 获取当前设备MAC地址
|
||||
String macAddress = NetUtil.getLocalMacAddress();
|
||||
log.debug("当前设备MAC地址:{}", macAddress);
|
||||
ApplicationCodePlaintext data = new ApplicationCodePlaintext();
|
||||
data.setMacAddress(macAddress);
|
||||
String plaintext = JSONUtil.toJsonStr(data);
|
||||
// RSA 加密
|
||||
try {
|
||||
return RSAUtil.encrypt(plaintext, RSAUtil.stringToPublicKey(props.getPublicKey()));
|
||||
} catch (Exception e) {
|
||||
log.error("申请码加密失败", e);
|
||||
throw new BusinessException(CommonResponseEnum.FAIL, "申请码生成失败");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public ActivationCodePlaintext verifyActivationCode(String activationCode) {
|
||||
String plaintext;
|
||||
try {
|
||||
plaintext = RSAUtil.decrypt(activationCode, RSAUtil.stringToPrivateKey(props.getPrivateKey()));
|
||||
} catch (Exception e) {
|
||||
log.error("授权码解密失败", e);
|
||||
throw new BusinessException(CommonResponseEnum.FAIL, "无效的激活码");
|
||||
}
|
||||
log.info("新授权码解密:{}", JSONUtil.toJsonStr(plaintext));
|
||||
ActivationCodePlaintext activationCodePlaintext = JSONUtil.toBean(plaintext, ActivationCodePlaintext.class);
|
||||
String macAddress = NetUtil.getLocalMacAddress();
|
||||
if (!StrUtil.equals(activationCodePlaintext.getMacAddress(), macAddress)) {
|
||||
log.error("mac地址不匹配");
|
||||
throw new BusinessException(CommonResponseEnum.FAIL, "无效的激活码");
|
||||
}
|
||||
return addOrUpdateLicenseFile(activationCodePlaintext, activationCode);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ActivationCodePlaintext readLicenseFile() {
|
||||
String licenseFilePath = props.getLicenseFilePath();
|
||||
log.info("读取授权文件,{}", licenseFilePath);
|
||||
if (FileUtil.exist(licenseFilePath)) {
|
||||
String content = FileUtil.readUtf8String(licenseFilePath);
|
||||
String plaintext;
|
||||
try {
|
||||
plaintext = RSAUtil.decrypt(content, RSAUtil.stringToPrivateKey(props.getPrivateKey()));
|
||||
} catch (Exception e) {
|
||||
log.error("授权文件内容解密失败", e);
|
||||
throw new BusinessException(CommonResponseEnum.FAIL, "许可信息读取失败");
|
||||
}
|
||||
return JSONUtil.toBean(plaintext, ActivationCodePlaintext.class);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String generateActivationCode(String applicationCode, ActivationCodePlaintext activationCodePlaintext) {
|
||||
// RSA 解密
|
||||
String plaintext;
|
||||
try {
|
||||
plaintext = RSAUtil.decrypt(applicationCode, RSAUtil.stringToPrivateKey(props.getPrivateKey()));
|
||||
} catch (Exception e) {
|
||||
log.error("申请码解密失败", e);
|
||||
throw new BusinessException(CommonResponseEnum.FAIL, "无效的申请码");
|
||||
}
|
||||
ApplicationCodePlaintext applicationCodePlaintext = JSONUtil.toBean(plaintext, ApplicationCodePlaintext.class);
|
||||
if (applicationCodePlaintext == null) {
|
||||
log.error("申请码内容为空");
|
||||
throw new BusinessException(CommonResponseEnum.FAIL, "无效的申请码");
|
||||
}
|
||||
String macAddress = applicationCodePlaintext.getMacAddress();
|
||||
if (StrUtil.isBlank(macAddress)) {
|
||||
log.error("mac地址为空");
|
||||
throw new BusinessException(CommonResponseEnum.FAIL, "无效的申请码");
|
||||
}
|
||||
// 激活码明文
|
||||
activationCodePlaintext.setMacAddress(applicationCodePlaintext.getMacAddress());
|
||||
// RSA 加密
|
||||
String jsonStr = JSONUtil.toJsonStr(activationCodePlaintext);
|
||||
log.info("生成激活码明文:{}", jsonStr);
|
||||
try {
|
||||
return RSAUtil.encrypt(jsonStr, RSAUtil.stringToPublicKey(props.getPublicKey()));
|
||||
} catch (Exception e) {
|
||||
log.error("生成激活码失败", e);
|
||||
throw new BusinessException(CommonResponseEnum.FAIL, "生成激活码失败");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加或更新授权文件
|
||||
*
|
||||
* @param newActivationCodePlaintext 新授权码明文
|
||||
* @param activationCode 授权码
|
||||
* @return 最新授权码明文
|
||||
*/
|
||||
private ActivationCodePlaintext addOrUpdateLicenseFile(ActivationCodePlaintext newActivationCodePlaintext, String activationCode) {
|
||||
|
||||
log.info("新授权码明文:{}", JSONUtil.toJsonStr(newActivationCodePlaintext));
|
||||
ActivationModule newContrast = newActivationCodePlaintext.getContrast();
|
||||
ActivationModule newDigital = newActivationCodePlaintext.getDigital();
|
||||
ActivationModule newSimulate = newActivationCodePlaintext.getSimulate();
|
||||
|
||||
String licenseFilePath = props.getLicenseFilePath();
|
||||
log.info("授权文件路径:{}", licenseFilePath);
|
||||
ActivationCodePlaintext oldActivationCodePlaintext = this.readLicenseFile();
|
||||
if (oldActivationCodePlaintext == null) {
|
||||
// 如果文件不存在,创建新文件
|
||||
FileUtil.touch(licenseFilePath);
|
||||
// 写入授权文件
|
||||
FileUtil.writeUtf8String(activationCode, licenseFilePath);
|
||||
return newActivationCodePlaintext;
|
||||
} else {
|
||||
log.info("旧授权码明文:{}", JSONUtil.toJsonStr(oldActivationCodePlaintext));
|
||||
oldActivationCodePlaintext.setMacAddress(newActivationCodePlaintext.getMacAddress());
|
||||
if (newContrast != null && newContrast.isPermanently()) {
|
||||
oldActivationCodePlaintext.setContrast(newContrast);
|
||||
}
|
||||
if (newDigital != null && newDigital.isPermanently()) {
|
||||
oldActivationCodePlaintext.setDigital(newDigital);
|
||||
}
|
||||
if (newSimulate != null && newSimulate.isPermanently()) {
|
||||
oldActivationCodePlaintext.setSimulate(newSimulate);
|
||||
}
|
||||
log.info("最新授权码明文:{}", JSONUtil.toJsonStr(oldActivationCodePlaintext));
|
||||
String updateContent;
|
||||
// 重新加密
|
||||
try {
|
||||
updateContent = RSAUtil.encrypt(JSONUtil.toJsonStr(oldActivationCodePlaintext), RSAUtil.stringToPublicKey(props.getPublicKey()));
|
||||
} catch (Exception e) {
|
||||
log.error("授权文件内容加密失败", e);
|
||||
throw new BusinessException(CommonResponseEnum.FAIL, "激活失败,请联系管理员");
|
||||
}
|
||||
FileUtil.writeUtf8String(updateContent, licenseFilePath);
|
||||
return oldActivationCodePlaintext;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
package com.njcn.gather.tool.active.vo;
|
||||
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@Data
|
||||
public class ActivationCodePlaintext extends ApplicationCodePlaintext {
|
||||
|
||||
/**
|
||||
* 模拟式模块
|
||||
*/
|
||||
private ActivationModule simulate;
|
||||
|
||||
/**
|
||||
* 数字式模块
|
||||
*/
|
||||
private ActivationModule digital;
|
||||
|
||||
/**
|
||||
* 比对式模块
|
||||
*/
|
||||
private ActivationModule contrast;
|
||||
|
||||
|
||||
public void init() {
|
||||
simulate = new ActivationModule();
|
||||
digital = new ActivationModule();
|
||||
contrast = new ActivationModule();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
package com.njcn.gather.tool.active.vo;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class ActivationModule {
|
||||
|
||||
/**
|
||||
* 是否永久授权
|
||||
*/
|
||||
private int permanently;
|
||||
|
||||
public boolean isPermanently() {
|
||||
return permanently == 1;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
package com.njcn.gather.tool.active.vo;
|
||||
|
||||
import io.swagger.annotations.ApiModel;
|
||||
import io.swagger.annotations.ApiModelProperty;
|
||||
import lombok.Data;
|
||||
|
||||
@ApiModel("申请码明文")
|
||||
@Data
|
||||
public class ApplicationCodePlaintext {
|
||||
/**
|
||||
* mac地址
|
||||
*/
|
||||
@ApiModelProperty(value = "mac地址", hidden = true)
|
||||
private String macAddress;
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user