13 KiB
13 KiB
授权文件功能演示说明
📸 功能界面说明
由于这是文本环境,以下用文字描述各功能的界面和操作流程。
1. 授权管理主页面
访问路径: http://localhost:3000/licenses
页面布局
┌─────────────────────────────────────────────────────────────┐
│ ← 授权管理 [导出] [选择授权项] │
│ 管理设备授权许可 │
├─────────────────────────────────────────────────────────────┤
│ ℹ️ 每个设备型号对应一套授权模块配置... │
├─────────────────────────────────────────────────────────────┤
│ 筛选条件: [设备型号▼] [查询] │
├─────────────────────────────────────────────────────────────┤
│ 设备型号 | 授权模块 | 操作 │
│ GD-30 | [一维] [二维] [三维] | [预览] [下载] [编辑] │
│ GD-20 | [一维] [二维] | [预览] [下载] [编辑] │
├─────────────────────────────────────────────────────────────┤
│ 共 2 条 [<] [1] [2] [>] │
└─────────────────────────────────────────────────────────────┘
操作按钮说明
- 预览 👁️ - 点击后弹出JSON预览窗口
- 下载 📄 - 直接下载JSON文件到本地
- 编辑 ✏️ - 修改授权配置
2. 授权文件预览弹窗
触发方式
点击列表中的"预览"按钮
弹窗内容
┌──────────────────────────────────────────────────────────┐
│ 授权文件预览 - GD-30 [X] │
├──────────────────────────────────────────────────────────┤
│ { │
│ "version": "1.0", │
│ "generatedAt": "2026-04-30T16:00:00.000Z", │
│ "deviceModel": "GD-30 Supreme", │
│ "deviceSN": "", │
│ "validUntil": "2027-04-30", │
│ "status": "active", │
│ "authModules": [ │
│ { │
│ "id": "1D", │
│ "name": "一维自电/电阻率/激电测试模块", │
│ "category": "一维", │
│ "enabled": true │
│ }, │
│ ... │
│ ], │
│ "config": { │
│ "name": "CFG-GD30-v2.1", │
│ "emissionParams": {...}, │
│ "acquisitionParams": {...} │
│ }, │
│ "signature": { │
│ "algorithm": "SHA256", │
│ "value": "a1b2c3d4...", │
│ "publicKey": "platform-public-key-placeholder" │
│ } │
│ } │
├──────────────────────────────────────────────────────────┤
│ [下载JSON] [关闭] │
└──────────────────────────────────────────────────────────┘
功能特点
- JSON格式化显示,带语法高亮
- 可滚动查看完整内容
- 支持一键下载JSON文件
- 点击背景或关闭按钮退出
3. 创建/编辑授权抽屉
触发方式
点击"选择授权项"按钮
抽屉内容
┌──────────────────────────────────────┐
│ 选择授权项 [X] │
├──────────────────────────────────────┤
│ 设备型号 │
│ [GD-30 Supreme ▼] │
│ │
│ 授权项目 [全选] [清空]│
│ ┌──────────────────────────────────┐ │
│ │ ☑ 分类 │ 名称 │ 说明 │ │
│ ├──────────────────────────────────┤ │
│ │ ☑ 一维 │ 一维自电... │ ... │ │
│ │ ☑ 二维 │ 二维自电... │ ... │ │
│ │ ☑ 三维 │ 三维自电... │ ... │ │
│ │ □ 水上 │ 水上... │ ... │ │
│ │ □ 跨孔 │ 跨孔... │ ... │ │
│ │ □ 电流场法│ 电流场法... │ ... │ │
│ └──────────────────────────────────┘ │
│ │
│ 已选择 3 项 │
│ │
│ ℹ️ 授权文件由选定的授权项与对应型号的 │
│ 配置文件共同生成... │
│ │
│ [保存] │
└──────────────────────────────────────┘
交互说明
- 点击行可选中/取消选中
- 支持全选/清空快捷操作
- 保存时自动生成JSON授权文件
4. 手机APP调用流程演示
场景:设备启动时获取授权
Step 1: APP发起请求
GET /api/licenses/download?sn=GD30-20260430-001
Step 2: 平台处理流程
接收请求
↓
验证设备SN是否存在
↓
查找设备级授权 (device_sn匹配)
↓ 未找到
查找型号级授权 (model匹配 + 状态生效 + 未过期)
↓
获取关联的配置文件
↓
生成/读取授权文件JSON
↓
记录下载日志 (时间、IP、APP版本)
↓
返回JSON响应
Step 3: APP接收响应
{
"version": "1.0",
"deviceModel": "GD-30 Supreme",
"deviceSN": "GD30-20260430-001",
"validUntil": "2027-04-30",
"status": "active",
"authModules": [
{"id": "1D", "name": "...", "enabled": true},
{"id": "2D", "name": "...", "enabled": true},
{"id": "3D", "name": "...", "enabled": true}
],
"config": {
"emissionParams": {"maxVoltage": "1000V", ...},
"acquisitionParams": {"channels": 12, ...}
},
"signature": {...}
}
Step 4: APP处理授权
// 1. 验证签名
if (!verifySignature(license)) {
throw new Error('授权文件被篡改');
}
// 2. 检查有效期
if (new Date(license.validUntil) < new Date()) {
throw new Error('授权已过期');
}
// 3. 启用功能模块
license.authModules.forEach(module => {
if (module.enabled) {
app.enableFeature(module.id);
}
});
// 4. 应用配置参数
app.applyConfig(license.config);
// 5. 显示授权信息
console.log(`授权有效期至: ${license.validUntil}`);
5. 数据库查询演示
查看授权记录
SELECT id, model, modules, expiry, status,
CASE WHEN license_file IS NOT NULL THEN '已生成' ELSE '未生成' END as file_status
FROM licenses
ORDER BY id DESC;
示例输出:
id | model | modules | expiry | status | file_status
1 | GD-30 | 一维, 二维, 三维| 2027-04-30 | 生效 | 已生成
2 | GD-20 | 一维, 二维 | 2027-04-30 | 生效 | 已生成
查看下载日志
SELECT l.device_sn, l.download_time, l.ip_address,
lic.model, lic.expiry
FROM license_download_logs l
JOIN licenses lic ON l.license_id = lic.id
ORDER BY l.download_time DESC
LIMIT 10;
示例输出:
device_sn | download_time | ip_address | model | expiry
GD30-20260430-001 | 2026-04-30 16:30:00 | 192.168.1.100 | GD-30 | 2027-04-30
GD30-20260430-002 | 2026-04-30 16:25:00 | 192.168.1.101 | GD-30 | 2027-04-30
6. 错误场景演示
场景A: 设备不存在
curl "http://localhost:3000/api/licenses/download?sn=INVALID-SN"
响应:
{
"error": "设备不存在"
}
HTTP状态码: 404
场景B: 无有效授权
curl "http://localhost:3000/api/licenses/download?sn=GD30-NO-LICENSE"
响应:
{
"error": "该设备暂无有效授权",
"deviceSN": "GD30-NO-LICENSE",
"deviceModel": "GD-30 Supreme"
}
HTTP状态码: 404
场景C: 缺少SN参数
curl "http://localhost:3000/api/licenses/download"
响应:
{
"error": "设备SN不能为空"
}
HTTP状态码: 400
7. 性能测试演示
测试工具: Apache Bench (ab)
# 模拟100个并发请求,总共1000次请求
ab -n 1000 -c 100 "http://localhost:3000/api/licenses/download?sn=GD30-TEST-001"
预期结果:
Requests per second: 500.00 [#/sec] (mean)
Time per request: 200.000 [ms] (mean)
Time per request: 2.000 [ms] (mean, across all concurrent requests)
优化建议
- 首次生成后缓存到数据库,避免重复计算
- 为licenses表添加索引:
CREATE INDEX idx_license_device_sn ON licenses(device_sn); CREATE INDEX idx_license_model ON licenses(model, status, expiry); - 生产环境使用Redis缓存热点授权
8. 安全测试演示
测试签名验证
// 正常授权文件
const validLicense = await fetchLicense('GD30-TEST-001');
console.log(verifySignature(validLicense)); // true
// 篡改后的授权文件
const tamperedLicense = {...validLicense, validUntil: '2099-12-31'};
console.log(verifySignature(tamperedLicense)); // false
测试SQL注入防护
# 尝试SQL注入
curl "http://localhost:3000/api/licenses/download?sn='; DROP TABLE devices; --"
结果:
- 使用参数化查询,SQL注入无效
- 返回"设备不存在"错误
9. 移动端适配说明
iOS App (Swift)
func fetchLicense(deviceSN: String, completion: @escaping (Result<License, Error>) -> Void) {
let url = URL(string: "http://your-server.com/api/licenses/download?sn=\(deviceSN)")!
URLSession.shared.dataTask(with: url) { data, response, error in
if let error = error {
completion(.failure(error))
return
}
guard let data = data,
let license = try? JSONDecoder().decode(License.self, from: data) else {
completion(.failure(NSError(domain: "Invalid response", code: -1)))
return
}
completion(.success(license))
}.resume()
}
Android App (Kotlin)
suspend fun fetchLicense(deviceSN: String): License {
val response = httpClient.get("http://your-server.com/api/licenses/download?sn=$deviceSN")
if (!response.isSuccess) {
throw IOException("Failed to fetch license")
}
return response.body()
}
10. 监控和告警
关键指标监控
-
API响应时间
- P95 < 500ms
- P99 < 1000ms
-
错误率
- 4xx错误 < 5%
- 5xx错误 < 1%
-
下载次数统计
- 每日下载总量
- 按设备型号分组统计
- 异常下载频率检测
告警规则
alerts:
- name: high_error_rate
condition: error_rate > 5%
duration: 5m
action: send_notification
- name: slow_response
condition: p95_latency > 1000ms
duration: 10m
action: send_notification
- name: unusual_download_pattern
condition: downloads_per_minute > 100
duration: 2m
action: send_notification_and_block_ip
提示: 实际使用时可以配合截图工具(如Snipaste、Greenshot)截取各个界面的实际效果,添加到文档中以便更直观地展示功能。