👉 这是一个或许对你有用的社群
🐱 一对一交流/面试小册/简历优化/求职解惑,欢迎加入「芋道快速开发平台」知识星球。下面是星球提供的部分资料:
《项目实战(视频)》:从书中学,往事上“练”
《互联网高频面试题》:面朝简历学习,春暖花开
《架构 x 系统设计》:摧枯拉朽,掌控面试高频场景题
《精进 Java 学习指南》:系统学习,互联网主流技术栈
《必读 Java 源码专栏》:知其然,知其所以然

👉这是一个或许对你有用的开源项目
国产Star破10w的开源项目,前端包括管理后台、微信小程序,后端支持单体、微服务架构
RBAC权限、数据权限、SaaS多租户、商城、支付、工作流、大屏报表、ERP、CRM、AI大模型、IoT物联网等功能:
多模块:https://gitee.com/zhijiantianya/ruoyi-vue-pro
微服务:https://gitee.com/zhijiantianya/yudao-cloud
视频教程:https://doc.iocoder.cn
【国内首批】支持 JDK17/21+SpringBoot3、JDK8/11+Spring Boot2双版本
背景与需求
在软件开发和商业化过程中,许可证控制是一个不可避免的技术需求。无论是企业级管理系统、桌面应用软件,还是SaaS服务,都需要对软件的使用范围、功能权限和时间限制进行有效管控。
许可证系统的核心价值在于:
保护知识产权: 防止软件被非法复制和分发
商业模式支撑: 支持按功能、按时间的差异化定价
用户管理: 精确控制授权用户和使用范围
合规要求: 满足企业对软件资产管理的需求
本文将介绍一个基于Spring Boot + RSA2048非对称加密的许可证控制系统实现方案,具备硬件绑定、功能权限控制等。
基于 Spring Boot + MyBatis Plus + Vue & Element 实现的后台管理系统 + 用户小程序,支持 RBAC 动态权限、多租户、数据权限、工作流、三方登录、支付、短信、商城等功能
项目地址:https://github.com/YunaiV/ruoyi-vue-pro
视频教程:https://doc.iocoder.cn/video/
设计思路
技术设计
许可证系统采用非对称加密的设计思路:厂商使用私钥对许可证信息进行数字签名,客户端使用对应的公钥验证签名的真实性。这种架构的优势在于:
安全性高: 私钥由厂商严格保管,公钥可以随软件分发,即使公钥泄露也无法伪造许可证
部署简单: 无需额外的许可证服务器,支持离线验证
扩展性强: 可以灵活添加各种验证规则和权限控制
技术选型
后端技术栈:
Spring Boot 3.x: 提供完整的Web服务框架和依赖注入
Java Security API: 利用JDK内置的RSA加密算法实现
Jackson: 处理JSON序列化和反序列化
前端技术栈
原生JavaScript: 无框架依赖,保持轻量级
TailwindCSS: 快速构建现代化UI界面
RESTful API: 标准化的前后端交互
加密算法:
RSA2048: 足够安全的非对称加密强度
SHA256withRSA: 数字签名算法
Base64: 签名结果编码格式
基于 Spring Cloud Alibaba + Gateway + Nacos + RocketMQ + Vue & Element 实现的后台管理系统 + 用户小程序,支持 RBAC 动态权限、多租户、数据权限、工作流、三方登录、支付、短信、商城等功能
项目地址:https://github.com/YunaiV/yudao-cloud
视频教程:https://doc.iocoder.cn/video/
核心功能实现
硬件指纹获取
硬件绑定是许可证系统的重要安全特性,通过获取主板序列号实现设备唯一性识别。
@Component
publicclass HardwareUtil {
privatestaticfinal Logger logger = LoggerFactory.getLogger(HardwareUtil.class);
/**
* 获取主板序列号,支持Windows和Linux系统
*/
public String getMotherboardSerial() {
String os = System.getProperty("os.name").toLowerCase();
try {
if (os.contains("windows")) {
return getWindowsMotherboardSerial();
} elseif (os.contains("linux")) {
return getLinuxMotherboardSerial();
} else {
logger.warn("不支持的操作系统: {}", os);
return"UNKNOWN";
}
} catch (Exception e) {
logger.error("获取主板序列号失败", e);
return"UNKNOWN";
}
}
/**
* Windows系统通过WMI命令获取主板序列号
*/
private String getWindowsMotherboardSerial() throws Exception {
Process process = Runtime.getRuntime().exec("wmic baseboard get serialnumber");
BufferedReader reader = new BufferedReader(
new InputStreamReader(process.getInputStream(), StandardCharsets.UTF_8)
);
String line;
while ((line = reader.readLine()) != null) {
line = line.trim();
if (!line.isEmpty() && !line.equals("SerialNumber")) {
logger.debug("Windows主板序列号: {}", line);
return line;
}
}
reader.close();
process.waitFor();
return"UNKNOWN";
}
/**
* Linux系统通过dmidecode命令获取主板序列号
*/
private String getLinuxMotherboardSerial() throws Exception {
try {
// 优先使用dmidecode命令
Process process = Runtime.getRuntime().exec("sudo dmidecode -s baseboard-serial-number");
BufferedReader reader = new BufferedReader(
new InputStreamReader(process.getInputStream(), StandardCharsets.UTF_8)
);
String line = reader.readLine();
reader.close();
process.waitFor();
if (line != null && !line.trim().isEmpty() && !line.contains("Not Specified")) {
logger.debug("Linux主板序列号: {}", line.trim());
return line.trim();
}
// 备选方案:读取系统文件
return getLinuxMotherboardFromSys();
} catch (Exception e) {
logger.error("dmidecode命令执行失败", e);
return getLinuxMotherboardFromSys();
}
}
/**
* 从/sys/class/dmi/id/board_serial文件读取主板序列号
*/
private String getLinuxMotherboardFromSys() {
try {
Process process = Runtime.getRuntime().exec("cat /sys/class/dmi/id/board_serial");
BufferedReader reader = new BufferedReader(
new InputStreamReader(process.getInputStream(), StandardCharsets.UTF_8)
);
String line = reader.readLine();
reader.close();
process.waitFor();
if (line != null && !line.trim().isEmpty()) {
logger.debug("Linux主板序列号(从sys读取): {}", line.trim());
return line.trim();
}
} catch (Exception e) {
logger.warn("从/sys文件读取失败", e);
}
return"UNKNOWN";
}
/**
* 获取系统信息摘要,用于调试和展示
*/
public String getSystemInfo() {
return String.format("操作系统: %s %s, 架构: %s, 主板序列号: %s",
System.getProperty("os.name"),
System.getProperty("os.version"),
System.getProperty("os.arch"),
getMotherboardSerial()
);
}
}
这个实现的关键点:
异常处理: 获取失败时返回"
UNKNOWN"而不是抛出异常,保证程序稳定性多重备选: Linux下优先使用
dmidecode,失败时尝试读取sys文件编码处理: 统一使用UTF-8编码避免乱码问题
日志记录: 详细记录获取过程,便于问题排查
RSA加密工具类
RSA加密是整个系统的安全基石,需要提供密钥生成、签名、验签等完整功能。
@Component
publicclass RSAUtil {
privatestaticfinal Logger logger = LoggerFactory.getLogger(RSAUtil.class);
privatestaticfinal String ALGORITHM = "RSA";
privatestaticfinal String SIGNATURE_ALGORITHM = "SHA256withRSA";
privatestaticfinalint KEY_SIZE = 2048;
/**
* 生成RSA密钥对
*/
public KeyPair generateKeyPair() throws Exception {
KeyPairGenerator keyGen = KeyPairGenerator.getInstance(ALGORITHM);
keyGen.initialize(KEY_SIZE);
KeyPair keyPair = keyGen.generateKeyPair();
logger.info("RSA密钥对生成成功,密钥长度: {} bits", KEY_SIZE);
return keyPair;
}
/**
* 使用私钥对数据进行数字签名
*/
public String sign(String data, PrivateKey privateKey) throws Exception {
Signature signature = Signature.getInstance(SIGNATURE_ALGORITHM);
signature.initSign(privateKey);
signature.update(data.getBytes(StandardCharsets.UTF_8));
byte[] signedBytes = signature.sign();
String result = Base64.getEncoder().encodeToString(signedBytes);
logger.debug("数据签名完成,原始数据长度: {}, 签名长度: {}", data.length(), result.length());
return result;
}
/**
* 使用公钥验证数字签名
*/
public boolean verify(String data, String signatureBase64, PublicKey publicKey) throws Exception {
Signature signature = Signature.getInstance(SIGNATURE_ALGORITHM);
signature.initVerify(publicKey);
signature.update(data.getBytes(StandardCharsets.UTF_8));
byte[] signatureBytes = Base64.getDecoder().decode(signatureBase64);
boolean isValid = signature.verify(signatureBytes);
logger.debug("签名验证结果: {}", isValid ? "通过" : "失败");
return isValid;
}
/**
* 将私钥转换为PEM格式字符串
*/
public String privateKeyToPem(PrivateKey privateKey) {
String encoded = Base64.getEncoder().encodeToString(privateKey.getEncoded());
return"-----BEGIN PRIVATE KEY-----\n" +
formatBase64String(encoded) +
"\n-----END PRIVATE KEY-----";
}
/**
* 将公钥转换为PEM格式字符串
*/
public String publicKeyToPem(PublicKey publicKey) {
String encoded = Base64.getEncoder().encodeToString(publicKey.getEncoded());
return"-----BEGIN PUBLIC KEY-----\n" +
formatBase64String(encoded) +
"\n-----END PUBLIC KEY-----";
}
/**
* 从PEM格式字符串加载私钥
*/
public PrivateKey loadPrivateKeyFromPem(String pemContent) throws Exception {
String privateKeyPEM = pemContent
.replaceAll("-----\\w+ PRIVATE KEY-----", "")
.replaceAll("\\s", "");
byte[] decoded = Base64.getDecoder().decode(privateKeyPEM);
PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(decoded);
KeyFactory keyFactory = KeyFactory.getInstance(ALGORITHM);
return keyFactory.generatePrivate(spec);
}
/**
* 从PEM格式字符串加载公钥
*/
public PublicKey loadPublicKeyFromPem(String pemContent) throws Exception {
String publicKeyPEM = pemContent
.replaceAll("-----\\w+ PUBLIC KEY-----", "")
.replaceAll("\\s", "");
byte[] decoded = Base64.getDecoder().decode(publicKeyPEM);
X509EncodedKeySpec spec = new X509EncodedKeySpec(decoded);
KeyFactory keyFactory = KeyFactory.getInstance(ALGORITHM);
return keyFactory.generatePublic(spec);
}
/**
* 格式化Base64字符串,每64个字符换行
*/
private String formatBase64String(String base64) {
StringBuilder formatted = new StringBuilder();
for (int i = 0; i < base64.length(); i += 64) {
formatted.append(base64, i, Math.min(i + 64, base64.length())).append("\n");
}
return formatted.toString().trim();
}
}
许可证数据模型
许可证实体类定义了许可证包含的所有信息字段,使用Jackson注解控制JSON序列化顺序。
@JsonPropertyOrder({"subject", "issuedTo", "hardwareId", "expireAt", "features"})
publicclass License {
private String subject; // 软件产品名称
private String issuedTo; // 许可证授权对象
private String hardwareId; // 绑定的硬件指纹
@JsonFormat(pattern = "yyyy-MM-dd")
private LocalDate expireAt; // 许可证到期时间
private List<String> features; // 授权的功能模块列表
private String signature; // 数字签名(序列化时排除)
public License() {}
public License(String subject, String issuedTo, String hardwareId,
LocalDate expireAt, List<String> features) {
this.subject = subject;
this.issuedTo = issuedTo;
this.hardwareId = hardwareId;
this.expireAt = expireAt;
this.features = features;
}
// 完整的getter和setter方法
public String getSubject() { return subject; }
public void setSubject(String subject) { this.subject = subject; }
public String getIssuedTo() { return issuedTo; }
public void setIssuedTo(String issuedTo) { this.issuedTo = issuedTo; }
public String getHardwareId() { return hardwareId; }
public void setHardwareId(String hardwareId) { this.hardwareId = hardwareId; }
public LocalDate getExpireAt() { return expireAt; }
public void setExpireAt(LocalDate expireAt) { this.expireAt = expireAt; }
public List<String> getFeatures() { return features; }
public void setFeatures(List<String> features) { this.features = features; }
public String getSignature() { return signature; }
public void setSignature(String signature) { this.signature = signature; }
@Override
public String toString() {
return"License{" +
"subject='" + subject + '\'' +
", issuedTo='" + issuedTo + '\'' +
", hardwareId='" + hardwareId + '\'' +
", expireAt=" + expireAt +
", features=" + features +
'}';
}
}
许可证服务核心逻辑
许可证服务是整个系统的业务核心,负责许可证的生成和验证逻辑。
@Service
publicclass LicenseService {
privatestaticfinal Logger logger = LoggerFactory.getLogger(LicenseService.class);
@Autowired
private RSAUtil rsaUtil;
@Autowired
private HardwareUtil hardwareUtil;
@Autowired
private ObjectMapper objectMapper;
/**
* 生成许可证文件
*/
public String generateLicense(License license, PrivateKey privateKey) throws Exception {
// 自动填充硬件指纹
if (license.getHardwareId() == null || license.getHardwareId().isEmpty()) {
String hardwareId = hardwareUtil.getMotherboardSerial();
license.setHardwareId(hardwareId);
logger.info("自动获取硬件指纹: {}", hardwareId);
}
// 创建标准化的JSON数据用于签名
String licenseData = createStandardizedLicenseJson(license);
logger.debug("待签名的许可证数据: {}", licenseData);
// 使用私钥对许可证数据进行签名
String signature = rsaUtil.sign(licenseData, privateKey);
// 创建包含签名的完整许可证
JsonNode jsonNode = objectMapper.readTree(licenseData);
((ObjectNode) jsonNode).put("signature", signature);
String result = objectMapper.writerWithDefaultPrettyPrinter().writeValueAsString(jsonNode);
logger.info("许可证生成成功,授权给: {}, 到期时间: {}",
license.getIssuedTo(), license.getExpireAt());
return result;
}
/**
* 验证许可证的有效性
*/
public LicenseVerifyResult verifyLicense(String licenseJson, PublicKey publicKey) {
try {
JsonNode jsonNode = objectMapper.readTree(licenseJson);
// 检查是否包含签名字段
if (!jsonNode.has("signature")) {
returnnew LicenseVerifyResult(false, "许可证缺少数字签名");
}
String signature = jsonNode.get("signature").asText();
// 移除签名字段,重构原始许可证数据
((ObjectNode) jsonNode).remove("signature");
License license = objectMapper.readValue(jsonNode.toString(), License.class);
// 重新生成标准化的JSON数据用于验证
String licenseData = createStandardizedLicenseJson(license);
logger.debug("验证用许可证数据: {}", licenseData);
// 验证数字签名
boolean signatureValid = rsaUtil.verify(licenseData, signature, publicKey);
if (!signatureValid) {
logger.warn("许可证数字签名验证失败");
returnnew LicenseVerifyResult(false, "许可证数字签名无效");
}
// 验证硬件指纹
String currentHardwareId = hardwareUtil.getMotherboardSerial();
if (!currentHardwareId.equals(license.getHardwareId())) {
logger.warn("硬件指纹不匹配 - 期望: {}, 实际: {}",
license.getHardwareId(), currentHardwareId);
returnnew LicenseVerifyResult(false,
String.format("硬件指纹不匹配。许可证绑定设备: %s, 当前设备: %s",
license.getHardwareId(), currentHardwareId));
}
// 验证许可证有效期
if (license.getExpireAt().isBefore(LocalDate.now())) {
logger.warn("许可证已过期 - 到期时间: {}, 当前时间: {}",
license.getExpireAt(), LocalDate.now());
returnnew LicenseVerifyResult(false,
String.format("许可证已过期。到期时间: %s, 当前时间: %s",
license.getExpireAt(), LocalDate.now()));
}
logger.info("许可证验证通过 - 授权对象: {}, 功能权限: {}",
license.getIssuedTo(), license.getFeatures());
returnnew LicenseVerifyResult(true, "许可证验证成功", license);
} catch (Exception e) {
logger.error("许可证验证过程发生异常", e);
returnnew LicenseVerifyResult(false, "许可证格式错误: " + e.getMessage());
}
}
/**
* 创建标准化的许可证JSON数据
* 手动构建JSON确保字段顺序一致,这是签名验证成功的关键
*/
private String createStandardizedLicenseJson(License license) throws Exception {
StringBuilder json = new StringBuilder();
json.append("{");
json.append("\"subject\":\"").append(escapeJson(license.getSubject())).append("\",");
json.append("\"issuedTo\":\"").append(escapeJson(license.getIssuedTo())).append("\",");
json.append("\"hardwareId\":\"").append(escapeJson(license.getHardwareId())).append("\",");
json.append("\"expireAt\":\"").append(license.getExpireAt().toString()).append("\",");
json.append("\"features\":[");
if (license.getFeatures() != null && !license.getFeatures().isEmpty()) {
for (int i = 0; i < license.getFeatures().size(); i++) {
if (i > 0) json.append(",");
json.append("\"").append(escapeJson(license.getFeatures().get(i))).append("\"");
}
}
json.append("]}");
return json.toString();
}
/**
* 转义JSON字符串中的特殊字符
*/
private String escapeJson(String str) {
if (str == null) return"";
return str.replace("\\", "\\\\")
.replace("\"", "\\\"")
.replace("\n", "\\n")
.replace("\r", "\\r")
.replace("\t", "\\t");
}
/**
* 许可证验证结果封装类
*/
publicstaticclass LicenseVerifyResult {
privatefinalboolean valid;
privatefinal String message;
privatefinal License license;
public LicenseVerifyResult(boolean valid, String message) {
this(valid, message, null);
}
public LicenseVerifyResult(boolean valid, String message, License license) {
this.valid = valid;
this.message = message;
this.license = license;
}
public boolean isValid() { return valid; }
public String getMessage() { return message; }
public License getLicense() { return license; }
}
}
这个服务类的关键设计点:
标准化JSON序列化: 手动构建JSON确保字段顺序固定,解决Jackson序列化顺序不一致的问题
全面的验证逻辑: 签名、硬件、时间三重验证确保安全性
详细的日志记录: 记录关键操作和异常,便于问题诊断
异常处理: 将所有异常转换为业务结果,保证系统稳定性
REST API接口设计
为了提供完整的许可证管理功能,设计了一套RESTful API接口。
@RestController
@RequestMapping("/api")
@CrossOrigin(origins = "*")
publicclass LicenseController {
privatestaticfinal Logger logger = LoggerFactory.getLogger(LicenseController.class);
@Autowired
private LicenseService licenseService;
@Autowired
private KeyManagementService keyManagementService;
@Autowired
private HardwareUtil hardwareUtil;
/**
* 生成新的RSA密钥对
*/
@PostMapping("/keys/generate")
public ResponseEntity<Map<String, Object>> generateKeys() {
try {
Map<String, String> keys = keyManagementService.generateKeyPair();
Map<String, Object> response = new HashMap<>();
response.put("success", true);
response.put("data", keys);
response.put("message", "密钥对生成成功");
logger.info("新的RSA密钥对生成成功");
return ResponseEntity.ok(response);
} catch (Exception e) {
logger.error("密钥生成失败", e);
Map<String, Object> response = new HashMap<>();
response.put("success", false);
response.put("message", "密钥生成失败: " + e.getMessage());
return ResponseEntity.badRequest().body(response);
}
}
/**
* 加载RSA密钥
*/
@PostMapping("/keys/load")
public ResponseEntity<Map<String, Object>> loadKeys(@RequestBody Map<String, String> request) {
try {
String privateKey = request.get("privateKey");
String publicKey = request.get("publicKey");
if (privateKey != null && !privateKey.trim().isEmpty()) {
keyManagementService.loadPrivateKey(privateKey);
logger.info("私钥加载成功");
}
if (publicKey != null && !publicKey.trim().isEmpty()) {
keyManagementService.loadPublicKey(publicKey);
logger.info("公钥加载成功");
}
Map<String, Object> response = new HashMap<>();
response.put("success", true);
response.put("message", "密钥加载成功");
return ResponseEntity.ok(response);
} catch (Exception e) {
logger.error("密钥加载失败", e);
Map<String, Object> response = new HashMap<>();
response.put("success", false);
response.put("message", "密钥加载失败: " + e.getMessage());
return ResponseEntity.badRequest().body(response);
}
}
/**
* 生成许可证
*/
@PostMapping("/license/generate")
public ResponseEntity<Map<String, Object>> generateLicense(@RequestBody License license) {
try {
if (!keyManagementService.isKeysLoaded()) {
Map<String, Object> response = new HashMap<>();
response.put("success", false);
response.put("message", "请先生成或加载RSA密钥");
return ResponseEntity.badRequest().body(response);
}
String licenseJson = licenseService.generateLicense(
license, keyManagementService.getCachedPrivateKey());
Map<String, Object> response = new HashMap<>();
response.put("success", true);
response.put("data", licenseJson);
response.put("message", "许可证生成成功");
logger.info("为 {} 生成许可证成功", license.getIssuedTo());
return ResponseEntity.ok(response);
} catch (Exception e) {
logger.error("许可证生成失败", e);
Map<String, Object> response = new HashMap<>();
response.put("success", false);
response.put("message", "许可证生成失败: " + e.getMessage());
return ResponseEntity.badRequest().body(response);
}
}
/**
* 验证许可证
*/
@PostMapping("/license/verify")
public ResponseEntity<Map<String, Object>> verifyLicense(@RequestBody Map<String, String> request) {
try {
String licenseJson = request.get("licenseJson");
if (licenseJson == null || licenseJson.trim().isEmpty()) {
Map<String, Object> response = new HashMap<>();
response.put("success", false);
response.put("message", "许可证内容不能为空");
return ResponseEntity.badRequest().body(response);
}
if (!keyManagementService.isKeysLoaded()) {
Map<String, Object> response = new HashMap<>();
response.put("success", false);
response.put("message", "请先加载公钥");
return ResponseEntity.badRequest().body(response);
}
LicenseService.LicenseVerifyResult result = licenseService.verifyLicense(
licenseJson, keyManagementService.getCachedPublicKey());
Map<String, Object> response = new HashMap<>();
response.put("success", result.isValid());
response.put("message", result.getMessage());
if (result.getLicense() != null) {
response.put("license", result.getLicense());
}
logger.info("许可证验证完成,结果: {}", result.isValid() ? "通过" : "失败");
return ResponseEntity.ok(response);
} catch (Exception e) {
logger.error("许可证验证过程异常", e);
Map<String, Object> response = new HashMap<>();
response.put("success", false);
response.put("message", "许可证验证失败: " + e.getMessage());
return ResponseEntity.badRequest().body(response);
}
}
/**
* 获取当前硬件信息
*/
@GetMapping("/hardware/info")
public ResponseEntity<Map<String, Object>> getHardwareInfo() {
Map<String, Object> response = new HashMap<>();
response.put("success", true);
Map<String, String> hardwareInfo = new HashMap<>();
hardwareInfo.put("motherboardSerial", hardwareUtil.getMotherboardSerial());
hardwareInfo.put("systemInfo", hardwareUtil.getSystemInfo());
hardwareInfo.put("osName", System.getProperty("os.name"));
hardwareInfo.put("osVersion", System.getProperty("os.version"));
hardwareInfo.put("osArch", System.getProperty("os.arch"));
response.put("data", hardwareInfo);
logger.debug("硬件信息查询完成");
return ResponseEntity.ok(response);
}
/**
* 检查密钥加载状态
*/
@GetMapping("/keys/status")
public ResponseEntity<Map<String, Object>> getKeysStatus() {
Map<String, Object> response = new HashMap<>();
response.put("success", true);
response.put("keysLoaded", keyManagementService.isKeysLoaded());
response.put("hasPrivateKey", keyManagementService.getCachedPrivateKey() != null);
response.put("hasPublicKey", keyManagementService.getCachedPublicKey() != null);
return ResponseEntity.ok(response);
}
}
应用场景与集成
许可证文件格式
生成的许可证是一个标准的JSON文件,包含所有必要的授权信息和数字签名:
{
"subject": "企业管理系统",
"issuedTo": "北京某某科技有限公司",
"hardwareId": "BFEBFBFF000906E9",
"expireAt": "2025-12-31",
"features": ["USER_MANAGEMENT", "REPORT_EXPORT", "DATA_ANALYSIS"],
"signature": "MEUCIQDxxx...完整的Base64签名"
}
字段说明:
subject: 软件产品的名称或标识
issuedTo: 许可证的授权对象(通常是公司名称)
hardwareId: 绑定的硬件指纹(主板序列号)
expireAt: 许可证的到期日期
features: 授权使用的功能模块列表
signature: 使用私钥生成的数字签名
功能权限控制
基于许可证的功能权限控制可以通过AOP切面和自定义注解实现
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public@interface RequireFeature {
/**
* 需要的功能权限
*/
String value();
/**
* 权限不足时的提示信息
*/
String message() default "功能未授权";
}
@Component
@Aspect
@Order(1)
publicclass LicenseFeatureAspect {
privatestaticfinal Logger logger = LoggerFactory.getLogger(LicenseFeatureAspect.class);
@Around("@annotation(requireFeature)")
public Object checkFeaturePermission(ProceedingJoinPoint joinPoint, RequireFeature requireFeature) throws Throwable {
// 获取当前许可证信息
License currentLicense = LicenseContext.getCurrentLicense();
if (currentLicense == null) {
logger.warn("访问需要授权的功能,但未找到有效许可证: {}", requireFeature.value());
thrownew LicenseException("系统未找到有效许可证,请联系管理员");
}
// 检查功能权限
if (currentLicense.getFeatures() == null ||
!currentLicense.getFeatures().contains(requireFeature.value())) {
logger.warn("功能权限不足 - 用户: {}, 需要权限: {}, 拥有权限: {}",
currentLicense.getIssuedTo(),
requireFeature.value(),
currentLicense.getFeatures());
thrownew LicenseException(requireFeature.message() + ": " + requireFeature.value());
}
logger.debug("功能权限验证通过: {}", requireFeature.value());
return joinPoint.proceed();
}
}
// 使用示例
@RestController
@RequestMapping("/api/report")
publicclass ReportController {
@GetMapping("/export")
@RequireFeature("REPORT_EXPORT")
public ResponseEntity<byte[]> exportReport(@RequestParam String format) {
// 报表导出功能实现
byte[] reportData = generateReport(format);
HttpHeaders headers = new HttpHeaders();
headers.add("Content-Disposition", "attachment; filename=report." + format);
return ResponseEntity.ok()
.headers(headers)
.body(reportData);
}
@PostMapping("/schedule")
@RequireFeature("REPORT_SCHEDULE")
public ResponseEntity<String> scheduleReport(@RequestBody ScheduleRequest request) {
// 定时报表功能实现
scheduleReportJob(request);
return ResponseEntity.ok("定时报表创建成功");
}
}
Web管理界面
本DEMO提供了一个完整的Web管理界面,具备以下功能:
硬件信息展示: 实时显示当前设备的硬件指纹和系统信息
密钥管理: 生成新的RSA密钥对,或加载现有密钥
许可证生成: 创建包含各种权限的许可证文件
许可证验证: 验证许可证的有效性和权限范围
主要的前端交互逻辑:
// API基础配置
const API_BASE = 'http://localhost:8080/api';
// 页面初始化
document.addEventListener('DOMContentLoaded', function() {
loadHardwareInfo();
checkKeyStatus();
setDefaultExpireDate();
});
// 生成许可证
async function generateLicense() {
try {
// 收集表单数据
const licenseData = {
subject: document.getElementById('subject').value.trim(),
issuedTo: document.getElementById('issuedTo').value.trim(),
expireAt: document.getElementById('expireAt').value,
features: document.getElementById('features').value.trim()
.split(',').map(f => f.trim()).filter(f => f)
};
// 数据验证
if (!licenseData.subject || !licenseData.issuedTo || !licenseData.expireAt) {
showToast('警告', '请填写所有必填字段', 'warning');
return;
}
showToast('处理中', '正在生成许可证...', 'info');
// 调用API生成许可证
const response = await fetch(`${API_BASE}/license/generate`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(licenseData)
});
const result = await response.json();
if (result.success) {
document.getElementById('generatedLicense').value = result.data;
showToast('成功', '许可证生成成功', 'success');
} else {
showToast('错误', result.message, 'error');
}
} catch (error) {
console.error('生成许可证失败:', error);
showToast('错误', '网络连接失败', 'error');
}
}
// 验证许可证
async function verifyLicense() {
try {
const licenseJson = document.getElementById('licenseToVerify').value.trim();
if (!licenseJson) {
showToast('警告', '请输入许可证内容', 'warning');
return;
}
showToast('处理中', '正在验证许可证...', 'info');
const response = await fetch(`${API_BASE}/license/verify`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ licenseJson })
});
const result = await response.json();
displayVerificationResult(result);
} catch (error) {
console.error('验证许可证失败:', error);
showToast('错误', '验证过程出错', 'error');
}
}
总结
本文介绍了一套基于Spring Boot + RSA2048的许可证控制系统实现方案,包含硬件指纹获取、数字签名验证、功能权限控制等核心功能,支持跨平台部署,提供完整的Web示例界面,适用于企业软件的商业化授权控制。
https://github.com/yuboon/java-examples/tree/master/springboot-lic
欢迎加入我的知识星球,全面提升技术能力。
👉 加入方式,“长按”或“扫描”下方二维码噢:

星球的内容包括:项目实战、面试招聘、源码解析、学习路线。





文章有帮助的话,在看,转发吧。
谢谢支持哟 (*^__^*)
1万+

被折叠的 条评论
为什么被折叠?



