license: fix device re-register appKey update, add license file parser
- DeviceService.register(): update appKey when device switches to a different app and adjust registered device counters for old/new appKey - LicenseAdminController: fix updateAppLicense parameter count mismatch - AppController: add POST /api/apps/license/parse endpoint for license file decryption - SecurityCenterView: add License file parser UI with upload and paste support - appApi: add parseLicenseFile() method Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
这个提交包含在:
父节点
ccb976c605
当前提交
843ed69f3c
0
common/NODE
普通文件
0
common/NODE
普通文件
@ -51,7 +51,7 @@ public class LicenseAdminController {
|
||||
}
|
||||
}
|
||||
AppLicenseEntity updated = appLicenseService.update(
|
||||
appKey, null, req.maxDevices(), newExpiresAt, clearExpiresAt, req.isActive(), req.remark());
|
||||
appKey, null, req.maxDevices(), newExpiresAt, clearExpiresAt, req.isActive(), req.remark(), null, null, null);
|
||||
return ResponseEntity.ok(ApiResponse.success(updated));
|
||||
}
|
||||
|
||||
|
||||
@ -58,8 +58,14 @@ public class DeviceService {
|
||||
// Re-issue token
|
||||
String token = licenseAuthService.generateToken(appKey, deviceId, existing.getId());
|
||||
String tokenHash = hashToken(token);
|
||||
if (existing.getAppKey() == null || existing.getAppKey().isBlank()) {
|
||||
String oldAppKey = existing.getAppKey();
|
||||
if (oldAppKey == null || oldAppKey.isBlank()) {
|
||||
existing.setAppKey(appKey);
|
||||
} else if (!oldAppKey.equals(appKey)) {
|
||||
// Device switched to a different app: update appKey and adjust counters
|
||||
existing.setAppKey(appKey);
|
||||
appLicenseService.decrementRegisteredDevices(oldAppKey);
|
||||
appLicenseService.incrementRegisteredDevices(appKey);
|
||||
}
|
||||
existing.setDeviceName(firstNonBlank(deviceName, existing.getDeviceName()));
|
||||
existing.setDeviceModel(firstNonBlank(deviceModel, existing.getDeviceModel()));
|
||||
|
||||
@ -2,6 +2,7 @@ package com.xuqm.tenant.controller;
|
||||
|
||||
import com.xuqm.common.model.ApiResponse;
|
||||
import com.xuqm.common.exception.BusinessException;
|
||||
import com.xuqm.common.security.LicenseFileCrypto;
|
||||
import com.xuqm.tenant.dto.CreateAppRequest;
|
||||
import com.xuqm.tenant.entity.AppEntity;
|
||||
import com.xuqm.tenant.entity.FeatureServiceEntity;
|
||||
@ -174,6 +175,34 @@ public class AppController {
|
||||
.body(encrypted.getBytes(java.nio.charset.StandardCharsets.UTF_8));
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse an uploaded license file and return its decrypted contents.
|
||||
* Used by the security center to verify license file information.
|
||||
*/
|
||||
@PostMapping("/license/parse")
|
||||
public ResponseEntity<ApiResponse<Map<String, Object>>> parseLicenseFile(@RequestBody Map<String, String> body) {
|
||||
String content = body.get("content");
|
||||
if (content == null || content.isBlank()) {
|
||||
throw new BusinessException("License file content is required");
|
||||
}
|
||||
try {
|
||||
LicenseFileCrypto.LicensePayload payload = LicenseFileCrypto.decrypt(content.trim());
|
||||
Map<String, Object> data = new java.util.LinkedHashMap<>();
|
||||
data.put("appKey", payload.appKey());
|
||||
data.put("appName", payload.appName());
|
||||
data.put("packageName", payload.packageName());
|
||||
data.put("iosBundleId", payload.iosBundleId());
|
||||
data.put("harmonyBundleName", payload.harmonyBundleName());
|
||||
data.put("baseUrl", payload.baseUrl());
|
||||
data.put("serverUrl", payload.serverUrl());
|
||||
return ResponseEntity.ok(ApiResponse.success(data));
|
||||
} catch (IllegalArgumentException e) {
|
||||
throw new BusinessException("Invalid license file: " + e.getMessage());
|
||||
} catch (Exception e) {
|
||||
throw new BusinessException("Failed to parse license file: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
private static String sanitizeFileName(String value) {
|
||||
String name = value == null || value.isBlank() ? "license" : value.trim();
|
||||
return name.replaceAll("[\\\\/:*?\"<>|\\s]+", "_");
|
||||
|
||||
正在加载...
在新工单中引用
屏蔽一个用户