商业化必备:SpringBoot 实现许可证控制
背景与需求
在软件开发和商业化过程中,许可证控制是一个不可避免的技术需求。无论是企业级管理系统、桌面应用软件,还是SaaS服务,都需要对软件的使用范围、功能权限和时间限制进行有效管控。
许可证系统的核心价值在于: 保护知识产权:防止软件被非法复制和分发
商业模式支撑:支持按功能、按时间的差异化定价
用户管理:精确控制授权用户和使用范围
合规要求:满足企业对软件资产管理的需求
本文将介绍一个基于Spring Boot + RSA2048非对称加密的许可证控制系统实现方案,具备硬件绑定、功能权限控制等。
设计思路
技术设计
许可证系统采用非对称加密的设计思路:厂商使用私钥对许可证信息进行数字签名,客户端使用对应的公钥验证签名的真实性。这种架构的优势在于:
1. 安全性高:私钥由厂商严格保管,公钥可以随软件分发,即使公钥泄露也无法伪造许可证
2. 部署简单:无需额外的许可证服务器,支持离线验证
3. 扩展性强:可以灵活添加各种验证规则和权限控制
技术选型
后端技术栈:
- Spring Boot 3.x:提供完整的Web服务框架和依赖注入
- Java Security API:利用JDK内置的RSA加密算法实现
- Jackson:处理JSON序列化和反序列化
前端技术栈
- 原生JavaScript:无框架依赖,保持轻量级
- TailwindCSS:快速构建现代化UI界面
- RESTful API:标准化的前后端交互
加密算法:
- RSA2048:足够安全的非对称加密强度
- SHA256withRSA:数字签名算法
- Base64:签名结果编码格式
核心功能实现
硬件指纹获取
硬件绑定是许可证系统的重要安全特性,通过获取主板序列号实现设备唯一性识别。
java
体验AI代码助手
代码解读
复制代码
@Component public class HardwareUtil { private static final 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(); } else if (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加密是整个系统的安全基石,需要提供密钥生成、签名、验签等完整功能。
java
体验AI代码助手
代码解读
复制代码
@Component public class RSAUtil { private static final Logger logger = LoggerFactory.getLogger(RSAUtil.class); private static final String ALGORITHM = "RSA"; private static final String SIGNATURE_ALGORITHM = "SHA256withRSA"; private static final int 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序列化顺序。
java
体验AI代码助手
代码解读
复制代码
@JsonPropertyOrder({"subject", "issuedTo", "hardwareId", "expireAt", "features"}) public class 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 + '}'; } }
许可证服务核心逻辑
许可证服务是整个系统的业务核心,负责许可证的生成和验证逻辑。
java
体验AI代码助手
代码解读
复制代码
@Service public class LicenseService { private static final 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")) { return new 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("许可证数字签名验证失败"); return new LicenseVerifyResult(false, "许可证数字签名无效"); } // 验证硬件指纹 String currentHardwareId = hardwareUtil.getMotherboardSerial(); if (!currentHardwareId.equals(license.getHardwareId())) { logger.warn("硬件指纹不匹配 - 期望: {}, 实际: {}", license.getHardwareId(), currentHardwareId); return new LicenseVerifyResult(false, String.format("硬件指纹不匹配。许可证绑定设备: %s, 当前设备: %s", license.getHardwareId(), currentHardwareId)); } // 验证许可证有效期 if (license.getExpireAt().isBefore(LocalDate.now())) { logger.warn("许可证已过期 - 到期时间: {}, 当前时间: {}", license.getExpireAt(), LocalDate.now()); return new LicenseVerifyResult(false, String.format("许可证已过期。到期时间: %s, 当前时间: %s", license.getExpireAt(), LocalDate.now())); } logger.info("许可证验证通过 - 授权对象: {}, 功能权限: {}", license.getIssuedTo(), license.getFeatures()); return new LicenseVerifyResult(true, "许可证验证成功", license); } catch (Exception e) { logger.error("许可证验证过程发生异常", e); return new 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"); } /** * 许可证验证结果封装类 */ public static class LicenseVerifyResult { private final boolean valid; private final String message; private final 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接口。
java
体验AI代码助手
代码解读
复制代码
@RestController @RequestMapping("/api") @CrossOrigin(origins = "*") public class LicenseController { private static final 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文件,包含所有必要的授权信息和数字签名:
json
体验AI代码助手
代码解读
复制代码
{ "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切面和自定义注解实现
java
体验AI代码助手
代码解读
复制代码
@Target({ElementType.METHOD, ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface RequireFeature { /** * 需要的功能权限 */ String value(); /** * 权限不足时的提示信息 */ String message() default "功能未授权"; } @Component @Aspect @Order(1) public class LicenseFeatureAspect { private static final 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()); throw new LicenseException("系统未找到有效许可证,请联系管理员"); } // 检查功能权限 if (currentLicense.getFeatures() == null || !currentLicense.getFeatures().contains(requireFeature.value())) { logger.warn("功能权限不足 - 用户: {}, 需要权限: {}, 拥有权限: {}", currentLicense.getIssuedTo(), requireFeature.value(), currentLicense.getFeatures()); throw new LicenseException(requireFeature.message() + ": " + requireFeature.value()); } logger.debug("功能权限验证通过: {}", requireFeature.value()); return joinPoint.proceed(); } } // 使用示例 @RestController @RequestMapping("/api/report") public class 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密钥对,或加载现有密钥
许可证生成:创建包含各种权限的许可证文件
许可证验证:验证许可证的有效性和权限范围
主要的前端交互逻辑:
javascript
体验AI代码助手
代码解读
复制代码
// 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示例界面,适用于企业软件的商业化授权控制。
更多推荐



所有评论(0)