# 软件授权系统 - 综合设计方案 ## 一、需求分析 ### 1.1 核心功能 | 模块 | 功能描述 | |------|----------| | **启动器客户端** | 卡密登录 → 验证 → 下载软件 → 启动软件 | | **管理后台** | 卡密生成/管理、多项目管理、统计报表 | | **API 服务** | 认证、授权、软件分发、心跳验证 | | **SDK(可选)** | 供第三方软件集成,实现授权验证 | ### 1.2 用户角色 | 角色 | 描述 | 权限 | |------|------|------| | **超级管理员** | 系统最高权限 | 全部功能 + 代理管理 | | **管理员** | 项目管理、卡密管理 | 所属项目的完整权限 | | **代理商** | 销售卡密、管理额度 | 限制生成数量、查看销售统计 | | **最终用户** | 使用卡密登录启动器 | 仅使用软件 | | **软件开发者** | 集成 SDK 开发授权软件 | 查看开发文档 | ### 1.3 功能清单 ``` ┌─────────────────────────────────────────────────────────────────┐ │ 管理后台功能 │ ├─────────────────────────────────────────────────────────────────┤ │ │ │ 📊 仪表盘 │ │ ├─ 实时统计(今日/本周/本月) │ │ ├─ 活跃用户趋势图 │ │ ├─ 项目分布饼图 │ │ └─ 最近活动日志 │ │ │ │ 📁 项目管理 │ │ ├─ 创建项目(自动生成 ProjectKey/Secret) │ │ ├─ 编辑项目信息 │ │ ├─ 上传软件包(自动加密) │ │ ├─ 查看项目统计 │ │ ├─ 查看开发文档(可编辑) │ │ └─ 删除/禁用项目 │ │ │ │ 🔑 卡密管理 │ │ ├─ 批量生成卡密 │ │ │ ├─ 选择项目 │ │ │ ├─ 选择类型(天卡/周卡/月卡/年卡/永久) │ │ │ ├─ 设置数量 │ │ │ ├─ 批量导出(TXT/CSV/Excel) │ │ │ └─ 预览生成结果 │ │ ├─ 卡密列表 │ │ │ ├─ 搜索(卡密/备注) │ │ │ ├─ 筛选(状态/类型/项目) │ │ │ ├─ 批量操作 │ │ │ └─ 导出 │ │ ├─ 卡密详情 │ │ │ ├─ 基本信息 │ │ │ ├─ 使用记录 │ │ │ ├─ 设备信息 │ │ │ ├─ 操作日志 │ │ │ └─ 编辑/封禁/删除 │ │ └─ 卡密操作 │ │ ├─ 封禁卡密 │ │ ├─ 解封卡密 │ │ ├─ 延长有效期 │ │ ├─ 修改备注 │ │ ├─ 重置设备绑定 │ │ └─ 删除卡密 │ │ │ │ 👥 代理商管理 │ │ ├─ 创建代理商 │ │ │ ├─ 设置账号密码 │ │ │ ├─ 设置初始额度 │ │ │ ├─ 设置折扣比例 │ │ │ └─ 分配可见项目 │ │ ├─ 代理商列表 │ │ ├─ 额度管理 │ │ │ ├─ 充值额度 │ │ │ ├─ 扣减额度 │ │ │ └─ 额度流水记录 │ │ ├─ 销售统计 │ │ └─ 禁用/删除代理商 │ │ │ │ 🖥️ 设备管理 │ │ ├─ 设备列表 │ │ ├─ 在线设备监控 │ │ ├─ 强制下线 │ │ └─ 解绑设备 │ │ │ │ 📝 日志审计 │ │ ├─ 操作日志 │ │ ├─ 登录日志 │ │ ├─ 验证日志 │ │ └─ 异常告警 │ │ │ │ 📖 开发文档 │ │ ├─ SDK 集成指南 │ │ ├─ API 接口文档 │ │ ├─ 代码示例 │ │ └─ 常见问题 │ │ │ │ ⚙️ 系统设置 │ │ ├─ 管理员账号管理 │ │ ├─ 系统配置 │ │ └─ 数据备份 │ │ │ └─────────────────────────────────────────────────────────────────┘ ``` --- ## 二、系统架构 ``` ┌─────────────────────────────────────────────────────────────────┐ │ 管理后台前端 │ │ (Vue3 + Element Plus) │ │ 项目管理 | 卡密生成 | 统计报表 | 日志审计 │ └─────────────────────────────┬───────────────────────────────────┘ │ HTTP/HTTPS ▼ ┌─────────────────────────────────────────────────────────────────┐ │ 后端 API 服务 │ │ (ASP.NET Core Web API) │ │ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │ │ │ 卡密管理 │ │ 项目管理 │ │ 统计分析 │ │ │ └──────────────┘ └──────────────┘ └──────────────┘ │ │ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │ │ │ 验证服务 │ │ 日志审计 │ │ 风控引擎 │ │ │ └──────────────┘ └──────────────┘ └──────────────┘ │ └─────────────────────────────┬───────────────────────────────────┘ │ ┌─────────────────────────────┴───────────────────────────────────┐ │ 数据层 │ │ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │ │ │ PostgreSQL │ │ Redis │ │ 文件存储 │ │ │ │ (持久化) │ │ (缓存/锁) │ │ (软件包) │ │ │ └──────────────┘ └──────────────┘ └──────────────┘ │ └─────────────────────────────────────────────────────────────────┘ ▲ │ API 调用 ┌─────────────────────────────┴───────────────────────────────────┐ │ │ │ ┌──────────────────┐ ┌──────────────────┐ │ │ │ 启动器客户端 │ │ 授权软件 │ │ │ │ (C# WPF) │ │ (C# + SDK) │ │ │ │ - 卡密验证 │ │ - 心跳验证 │ │ │ │ - 软件下载 │ │ - 机器码绑定 │ │ │ └──────────────────┘ └──────────────────┘ │ │ │ └──────────────────────────────────────────────────────────────────┘ ``` --- ## 三、数据库设计 ### 3.1 Projects(项目表) ```sql CREATE TABLE Projects ( Id SERIAL PRIMARY KEY, ProjectId VARCHAR(32) UNIQUE NOT NULL, -- 前端展示的项目ID ProjectKey VARCHAR(64) NOT NULL, -- 项目密钥(HMAC签名用) ProjectSecret VARCHAR(64) NOT NULL, -- 项目密钥(服务端验证用) Name VARCHAR(100) NOT NULL, Description TEXT, IconUrl VARCHAR(500), -- 项目图标 MaxDevices INT DEFAULT 1, -- 每卡密最大设备数 AutoUpdate BOOLEAN DEFAULT TRUE, -- 是否支持自动更新 IsEnabled BOOLEAN DEFAULT TRUE, CreatedBy INT, -- 创建人ID CreatedAt TIMESTAMP DEFAULT CURRENT_TIMESTAMP, UpdatedAt TIMESTAMP DEFAULT CURRENT_TIMESTAMP ); -- 项目卡密定价表 CREATE TABLE ProjectPricing ( Id SERIAL PRIMARY KEY, ProjectId VARCHAR(32) REFERENCES Projects(ProjectId) ON DELETE CASCADE, CardType VARCHAR(20) NOT NULL, -- day/week/month/year/lifetime DurationDays INT NOT NULL, OriginalPrice DECIMAL(10,2) NOT NULL, -- 原价 IsEnabled BOOLEAN DEFAULT TRUE, CreatedAt TIMESTAMP DEFAULT CURRENT_TIMESTAMP, UpdatedAt TIMESTAMP DEFAULT CURRENT_TIMESTAMP, UNIQUE(ProjectId, CardType, DurationDays) ); -- 软件版本表 CREATE TABLE SoftwareVersions ( Id SERIAL PRIMARY KEY, ProjectId VARCHAR(32) REFERENCES Projects(ProjectId) ON DELETE CASCADE, Version VARCHAR(20) NOT NULL, FileUrl VARCHAR(500) NOT NULL, -- 加密后的文件地址 FileSize BIGINT, -- 文件大小(字节) FileHash VARCHAR(64), -- SHA256哈希 EncryptionKey VARCHAR(256), -- 加密密钥(RSA加密后存储) Changelog TEXT, -- 更新日志 IsForceUpdate BOOLEAN DEFAULT FALSE, -- 是否强制更新 IsStable BOOLEAN DEFAULT TRUE, -- 是否稳定版本 PublishedAt TIMESTAMP DEFAULT CURRENT_TIMESTAMP, CreatedBy INT, CreatedAt TIMESTAMP DEFAULT CURRENT_TIMESTAMP, UNIQUE(ProjectId, Version) ); ``` ### 3.2 CardKeys(卡密表) ```sql CREATE TABLE CardKeys ( Id SERIAL PRIMARY KEY, ProjectId VARCHAR(32) REFERENCES Projects(ProjectId) ON DELETE SET NULL, KeyCode VARCHAR(32) UNIQUE NOT NULL, -- 卡密(格式:XXXX-XXXX-XXXX-XXXX) CardType VARCHAR(20) NOT NULL, -- day/week/month/year/lifetime DurationDays INT NOT NULL, ExpireTime TIMESTAMP, -- 过期时间 MaxDevices INT DEFAULT 1, -- 可单独覆盖项目配置 MachineCode VARCHAR(64), -- 首次激活后绑定的机器码 Status VARCHAR(20) DEFAULT 'unused', -- unused/active/expired/banned ActivateTime TIMESTAMP, LastUsedAt TIMESTAMP, -- 最后使用时间 UsedDuration BIGINT DEFAULT 0, -- 已使用时长(秒) GeneratedBy INT REFERENCES Admins(Id), -- 生成人 AgentId INT REFERENCES Agents(Id), -- 所属代理商(可空) SoldPrice DECIMAL(10,2), -- 实际售价(代理商生成时记录) Note VARCHAR(200), -- 备注 BatchId VARCHAR(36), -- 批次ID(用于批量管理) Version INT DEFAULT 1, -- 乐观锁版本号 CreatedAt TIMESTAMP DEFAULT CURRENT_TIMESTAMP, DeletedAt TIMESTAMP -- 软删除时间(NULL=未删除) ); -- 基础索引 CREATE INDEX idx_card_keys_project ON CardKeys(ProjectId); CREATE INDEX idx_card_keys_code ON CardKeys(KeyCode); CREATE INDEX idx_card_keys_status ON CardKeys(Status); -- 复合索引优化查询 CREATE INDEX idx_card_keys_project_status_created ON CardKeys(ProjectId, Status, CreatedAt DESC) WHERE DeletedAt IS NULL; CREATE INDEX idx_card_keys_expire ON CardKeys(ExpireTime) WHERE Status = 'active' AND DeletedAt IS NULL; CREATE INDEX idx_card_keys_agent ON CardKeys(AgentId) WHERE DeletedAt IS NULL; CREATE INDEX idx_card_keys_batch ON CardKeys(BatchId); ``` ### 3.3 Devices(设备表) ```sql CREATE TABLE Devices ( Id SERIAL PRIMARY KEY, CardKeyId INT REFERENCES CardKeys(Id) ON DELETE CASCADE, DeviceId VARCHAR(64) NOT NULL, -- 机器码(硬件指纹) DeviceName VARCHAR(100), -- 设备名称 OsInfo VARCHAR(100), -- 操作系统信息 LastHeartbeat TIMESTAMP, -- 最后心跳时间 IpAddress VARCHAR(45), -- 最后登录IP Location VARCHAR(100), -- IP归属地(省市) IsActive BOOLEAN DEFAULT TRUE, FirstLoginAt TIMESTAMP DEFAULT CURRENT_TIMESTAMP, DeletedAt TIMESTAMP -- 软删除时间 ); CREATE INDEX idx_devices_card_key ON Devices(CardKeyId); CREATE INDEX idx_devices_device_id ON Devices(DeviceId); CREATE INDEX idx_devices_heartbeat ON Devices(LastHeartbeat) WHERE IsActive = true AND DeletedAt IS NULL; ``` ### 3.4 AccessLogs(访问日志表) ```sql CREATE TABLE AccessLogs ( Id SERIAL PRIMARY KEY, ProjectId VARCHAR(32), CardKeyId INT, DeviceId VARCHAR(64), Action VARCHAR(50), -- login/verify/download/launch/heartbeat IpAddress INET, UserAgent TEXT, ResponseCode INT, -- 响应码(200/403等) ResponseTime INT, -- 响应时间(毫秒) CreatedAt TIMESTAMP DEFAULT CURRENT_TIMESTAMP ); CREATE INDEX idx_logs_project ON AccessLogs(ProjectId); CREATE INDEX idx_logs_created ON AccessLogs(CreatedAt); CREATE INDEX idx_logs_action_created ON AccessLogs(Action, CreatedAt DESC); CREATE INDEX idx_logs_card_key ON AccessLogs(CardKeyId); -- 日志表建议按月分区(数据量大时) -- CREATE TABLE AccessLogs_202501 PARTITION OF AccessLogs FOR VALUES FROM ('2025-01-01') TO ('2025-02-01'); ``` ### 3.5 Statistics(统计表) ```sql CREATE TABLE Statistics ( Id SERIAL PRIMARY KEY, ProjectId VARCHAR(32), Date DATE, ActiveUsers INT DEFAULT 0, NewUsers INT DEFAULT 0, TotalDownloads INT DEFAULT 0, TotalDuration BIGINT DEFAULT 0, -- 使用时长(秒) Revenue DECIMAL(10,2) DEFAULT 0, -- 收入 UNIQUE(ProjectId, Date) ); ``` ### 3.6 Admins(管理员表) ```sql CREATE TABLE Admins ( Id SERIAL PRIMARY KEY, Username VARCHAR(50) UNIQUE NOT NULL, PasswordHash VARCHAR(255) NOT NULL, -- bcrypt Email VARCHAR(100), Role VARCHAR(20) DEFAULT 'admin', -- super_admin/admin Permissions TEXT, -- JSON 数组,权限列表 Status VARCHAR(20) DEFAULT 'active', -- active/disabled LastLoginAt TIMESTAMP, LastLoginIp VARCHAR(45), CreatedAt TIMESTAMP DEFAULT CURRENT_TIMESTAMP, UpdatedAt TIMESTAMP DEFAULT CURRENT_TIMESTAMP ); ``` ### 3.7 Agents(代理商表) ```sql CREATE TABLE Agents ( Id SERIAL PRIMARY KEY, AdminId INT REFERENCES Admins(Id) ON DELETE SET NULL, AgentCode VARCHAR(20) UNIQUE NOT NULL, -- 代理商代码 CompanyName VARCHAR(100), ContactPerson VARCHAR(50), ContactPhone VARCHAR(20), ContactEmail VARCHAR(100), PasswordHash VARCHAR(255) NOT NULL, -- 代理商登录密码 Balance DECIMAL(10,2) DEFAULT 0, -- 额度余额 Discount DECIMAL(5,2) DEFAULT 100.00, -- 折扣比例(100=原价) CreditLimit DECIMAL(10,2) DEFAULT 0, -- 信用额度(负数=允许透支) MaxProjects INT DEFAULT 0, -- 0=全部,否则限制可见项目数 AllowedProjects TEXT, -- JSON 数组,可见的项目ID列表 Status VARCHAR(20) DEFAULT 'active', -- active/disabled LastLoginAt TIMESTAMP, CreatedAt TIMESTAMP DEFAULT CURRENT_TIMESTAMP, UpdatedAt TIMESTAMP DEFAULT CURRENT_TIMESTAMP ); ``` ### 3.8 AgentTransactions(代理商额度流水) ```sql CREATE TABLE AgentTransactions ( Id SERIAL PRIMARY KEY, AgentId INT REFERENCES Agents(Id) ON DELETE CASCADE, Type VARCHAR(20) NOT NULL, -- recharge/consume/refund Amount DECIMAL(10,2) NOT NULL, -- 正数=增加,负数=扣减 BalanceBefore DECIMAL(10,2) NOT NULL, -- 操作前余额 BalanceAfter DECIMAL(10,2) NOT NULL, -- 操作后余额 CardKeyId INT REFERENCES CardKeys(Id) ON DELETE SET NULL, Remark VARCHAR(200), CreatedBy INT REFERENCES Admins(Id) ON DELETE SET NULL, CreatedAt TIMESTAMP DEFAULT CURRENT_TIMESTAMP ); ``` ### 3.9 CardKeyLogs(卡密操作日志) ```sql CREATE TABLE CardKeyLogs ( Id SERIAL PRIMARY KEY, CardKeyId INT REFERENCES CardKeys(Id) ON DELETE CASCADE, Action VARCHAR(50) NOT NULL, -- create/activate/ban/unban/extend/reset_device OperatorId INT, -- 操作人(可为空,系统操作时) OperatorType VARCHAR(20), -- admin/agent/system Details TEXT, -- JSON 格式详情 IpAddress VARCHAR(45), CreatedAt TIMESTAMP DEFAULT CURRENT_TIMESTAMP ); ``` ### 3.10 SystemConfigs(系统配置表 - 配置驱动核心) ```sql CREATE TABLE SystemConfigs ( Id SERIAL PRIMARY KEY, ConfigKey VARCHAR(50) UNIQUE NOT NULL, ConfigValue TEXT, ValueType VARCHAR(20) DEFAULT 'string', -- string/bool/number/json Category VARCHAR(50) DEFAULT 'general', -- 配置分类 DisplayName VARCHAR(100), -- 前端显示名称 Description VARCHAR(500), -- 说明 Options TEXT, -- JSON,可选值(枚举类型用) IsPublic BOOLEAN DEFAULT FALSE, -- 是否可被客户端读取 UpdatedAt TIMESTAMP DEFAULT CURRENT_TIMESTAMP ); ``` **配置分类 + 前端开关控制:** | 分类 | 配置键 | 默认值 | 前端控制类型 | 说明 | |------|--------|--------|-------------|------| | **功能开关** | | 功能 | `feature.heartbeat` | true | 开关 | 是否启用心跳验证 | | 功能 | `feature.device_bind` | true | 开关 | 是否启用设备绑定 | | 功能 | `feature.auto_update` | true | 开关 | 是否启用自动更新 | | 功能 | `feature.force_update` | false | 开关 | 是否强制更新 | | 功能 | `feature.agent_system` | true | 开关 | 是否启用代理商系统 | | 功能 | `feature.card_renewal` | true | 开关 | 是否启用卡密续费 | | 功能 | `feature.trial_mode` | false | 开关 | 是否启用试用模式 | | **验证规则** | | 验证 | `auth.max_devices` | 1 | 数字 | 单卡密最大设备数 | | 验证 | `auth.allow_multi_device` | false | 开关 | 是否允许多设备同时在线 | | 验证 | `auth.need_activate` | true | 开关 | 卡密是否需要激活 | | 验证 | `auth.expire_type` | `activate` | 选择 | 过期类型:activate/fix | | **心跳配置** | | 心跳 | `heartbeat.enabled` | true | 开关 | 是否启用心跳 | | 心跳 | `heartbeat.interval` | 60 | 数字 | 心跳间隔(秒) | | 心跳 | `heartbeat.timeout` | 180 | 数字 | 心跳超时(秒) | | 心跳 | `heartbeat.offline_action` | `exit` | 选择 | 掉线后行为:exit/warning/none | | **限流配置** | | 限流 | `ratelimit.enabled` | true | 开关 | 是否启用限流 | | 限流 | `ratelimit.ip_per_minute` | 100 | 数字 | IP每分钟请求限制 | | 限流 | `ratelimit.device_per_minute` | 50 | 数字 | 设备每分钟请求限制 | | 限流 | `ratelimit.block_duration` | 5 | 数字 | 封禁时长(分钟) | | **风控配置** | | 风控 | `risk.enabled` | true | 开关 | 是否启用风控 | | 风控 | `risk.check_location` | true | 开关 | 异地登录检测 | | 风控 | `risk.check_device_change` | true | 开关 | 设备变更检测 | | 风控 | `risk.auto_ban` | false | 开关 | 自动封禁异常 | | **客户端显示** | | 显示 | `client.notice_title` | "" | 文本 | 公告标题 | | 显示 | `client.notice_content` | "" | 文本域 | 公告内容 | | 显示 | `client.contact_url` | "" | 文本 | 联系方式URL | | 显示 | `client.help_url` | "" | 文本 | 帮助文档URL | | 显示 | `client.show_balance` | false | 开关 | 是否在客户端显示余额 | | **其他** | | 系统 | `system.site_name` | "软件授权系统" | 文本 | 系统名称 | | 系统 | `system.logo_url` | "" | 文本 | Logo地址 | | 系统 | `system.enable_register` | false | 开关 | 是否开放用户注册 | | 系统 | `log.retention_days` | 90 | 数字 | 日志保留天数 | ``` ### 3.11 并发安全与事务处理 #### 3.11.1 代理商扣款(行级锁) ```sql -- 代理商生成卡密时扣款(必须使用事务+行级锁) BEGIN; -- 1. 获取行级锁,防止并发扣款 SELECT Balance FROM Agents WHERE Id = :agentId FOR UPDATE; -- 2. 检查余额是否充足 -- (在应用层判断,不足则 ROLLBACK) -- 3. 扣款 UPDATE Agents SET Balance = Balance - :amount, UpdatedAt = NOW() WHERE Id = :agentId AND Balance >= :amount; -- 4. 记录流水 INSERT INTO AgentTransactions (AgentId, Type, Amount, BalanceBefore, BalanceAfter, Remark, CreatedAt) VALUES (:agentId, 'consume', -:amount, :balanceBefore, :balanceAfter, :remark, NOW()); -- 5. 生成卡密 INSERT INTO CardKeys (...) VALUES (...); COMMIT; ``` #### 3.11.2 卡密激活(乐观锁) ```sql -- 使用乐观锁防止并发激活 UPDATE CardKeys SET Status = 'active', ActivateTime = NOW(), MachineCode = :machineCode, Version = Version + 1 WHERE KeyCode = :keyCode AND Status = 'unused' AND Version = :expectedVersion AND DeletedAt IS NULL; -- 检查受影响行数,0表示已被其他请求激活或版本冲突 -- 应用层需要处理这种情况 ``` #### 3.11.3 设备绑定(唯一约束+UPSERT) ```sql -- 设备绑定使用 UPSERT 避免并发插入冲突 INSERT INTO Devices (CardKeyId, DeviceId, DeviceName, OsInfo, IpAddress, LastHeartbeat, FirstLoginAt) VALUES (:cardKeyId, :deviceId, :deviceName, :osInfo, :ipAddress, NOW(), NOW()) ON CONFLICT (CardKeyId, DeviceId) DO UPDATE SET LastHeartbeat = NOW(), IpAddress = :ipAddress, DeviceName = COALESCE(:deviceName, Devices.DeviceName); ``` #### 3.11.4 心跳更新(批量优化) ```sql -- 批量更新心跳时间(减少数据库压力) UPDATE Devices SET LastHeartbeat = NOW() WHERE Id = ANY(:deviceIds) AND DeletedAt IS NULL; ``` #### 3.11.5 软删除查询模式 ```sql -- 所有查询默认过滤已删除数据 SELECT * FROM CardKeys WHERE ProjectId = :projectId AND DeletedAt IS NULL; -- 软删除操作 UPDATE CardKeys SET DeletedAt = NOW() WHERE Id = :id; -- 恢复删除 UPDATE CardKeys SET DeletedAt = NULL WHERE Id = :id; -- 清理30天前的已删除数据(定时任务) DELETE FROM CardKeys WHERE DeletedAt < NOW() - INTERVAL '30 days'; ``` ### 3.12 幂等性支持表 ```sql -- 幂等性键存储(防止重复请求) CREATE TABLE IdempotencyKeys ( Id SERIAL PRIMARY KEY, IdempotencyKey VARCHAR(64) UNIQUE NOT NULL, -- 幂等键(UUID) RequestPath VARCHAR(200) NOT NULL, -- 请求路径 RequestHash VARCHAR(64), -- 请求体哈希 ResponseCode INT, -- 响应状态码 ResponseBody TEXT, -- 响应内容(JSON) CreatedAt TIMESTAMP DEFAULT CURRENT_TIMESTAMP, ExpiresAt TIMESTAMP NOT NULL -- 过期时间 ); CREATE INDEX idx_idempotency_key ON IdempotencyKeys(IdempotencyKey); CREATE INDEX idx_idempotency_expires ON IdempotencyKeys(ExpiresAt); -- 定时清理过期的幂等键 DELETE FROM IdempotencyKeys WHERE ExpiresAt < NOW(); ``` --- ## 四、API 接口设计 ### 4.0 统一响应格式与错误码 #### 4.0.1 统一响应格式 ```json { "code": 200, "message": "success", "data": {}, "timestamp": 1735000000 } ``` #### 4.0.2 错误码规范 | 错误码 | 错误消息 | HTTP状态码 | 说明 | |--------|---------|-----------|------| | **成功类** | | 200 | success | 200 | 请求成功 | | **客户端错误(4xxx)** | | 400 | bad_request | 400 | 请求参数错误 | | 401 | unauthorized | 401 | 未授权,需要登录 | | 403 | forbidden | 403 | 无权限访问 | | 404 | not_found | 404 | 资源不存在 | | 1001 | card_invalid | 400 | 卡密格式错误或不存在 | | 1002 | card_expired | 403 | 卡密已过期 | | 1003 | card_banned | 403 | 卡密已被封禁 | | 1004 | card_already_used | 400 | 卡密已被使用 | | 1005 | device_limit_exceeded | 403 | 设备数量超过限制 | | 1006 | device_not_found | 404 | 设备未找到 | | 1007 | signature_invalid | 403 | 签名验证失败 | | 1008 | timestamp_expired | 400 | 时间戳过期 | | 1009 | rate_limit_exceeded | 429 | 请求过于频繁 | | 1010 | invalid_version | 400 | 客户端版本过低 | | 1011 | project_disabled | 403 | 项目已被禁用 | | 1012 | force_update_required | 426 | 需要强制更新 | | **服务端错误(5xxx)** | | 500 | internal_error | 500 | 服务器内部错误 | | 5001 | database_error | 500 | 数据库错误 | | 5002 | cache_error | 500 | 缓存服务错误 | | 5003 | storage_error | 500 | 文件存储错误 | | 5004 | encryption_error | 500 | 加密服务错误 | #### 4.0.3 幂等性设计 对于可能产生副作用的接口(如生成卡密、扣款),支持幂等性请求: ``` 请求头: X-Idempotency-Key: # 唯一请求标识,24小时内有效 示例: POST /api/admin/cards/generate Headers: Authorization: Bearer X-Idempotency-Key: 550e8400-e29b-41d4-a716-446655440000 重复请求同一 Idempotency-Key 将返回首次请求的响应结果。 ``` #### 4.0.4 分页参数规范 ``` 请求参数: page: 页码,从1开始(默认1) pageSize: 每页条数(默认20,最大100) 响应格式: { "code": 200, "data": { "items": [...], "pagination": { "page": 1, "pageSize": 20, "total": 1000, "totalPages": 50 } } } ``` #### 4.0.5 健康检查接口 ``` # 存活检查(用于K8s/Docker健康检查) GET /health/live Response: { "status": "ok" } # 就绪检查(检查数据库、Redis连接) GET /health/ready Response: { "status": "ok", "checks": { "database": "ok", "redis": "ok" } } # 失败时返回 503: Response: { "status": "error", "checks": { "database": "ok", "redis": "error: connection refused" } } ``` ### 4.1 客户端/SDK 接口 #### 4.1.1 卡密验证 ``` POST /api/auth/verify Request: { "projectId": "PROJ_001", "keyCode": "A3D7-K2P9-M8N1", "deviceId": "SHA256硬件指纹", "clientVersion": "1.0.0", "timestamp": 1735000000, "signature": "HMAC-SHA256签名" } Response: { "code": 200, "message": "success", "data": { "valid": true, "expireTime": "2025-12-31T23:59:59Z", "remainingDays": 30, "downloadUrl": "https://cdn.example.com/software_v1.0.bin", "fileHash": "sha256:xxxxx", "version": "1.0.0", "heartbeatInterval": 60, "accessToken": "jwt_token" } } ``` #### 4.1.2 心跳验证 ``` POST /api/auth/heartbeat Request: { "accessToken": "jwt_token", "deviceId": "xxx", "timestamp": 1735000000, "signature": "xxx" } Response: { "code": 200, "data": { "valid": true, "remainingDays": 30, "serverTime": 1735000000 } } 错误响应: { "code": 403, "message": "card_expired", "data": { "reason": "卡密已过期,请续费", "kick": true } } ``` #### 4.1.3 版本检查(云更新) ``` POST /api/software/check-update Request: { "projectId": "PROJ_001", "currentVersion": "1.0.0", "platform": "windows" } Response: { "code": 200, "data": { "hasUpdate": true, "latestVersion": "1.2.0", "forceUpdate": false, "downloadUrl": "https://cdn.example.com/software_v1.2.0.bin", "fileSize": 52428800, "fileHash": "sha256:xxxxx", "changelog": "- 修复若干bug\n- 新增xxx功能" } } ``` #### 4.1.4 软件下载 ``` GET /api/software/download?version=1.2.0&token=jwt_token Response: 加密的二进制文件 Headers: X-File-Hash: sha256校验值 X-File-Size: 文件大小 X-Encryption-Method: AES-256-GCM X-Encryption-Key: RSA加密的AES密钥(Base64) X-Encryption-Nonce: 随机nonce ``` ### 4.2 管理后台接口 #### 4.2.1 认证接口 ``` POST /api/admin/login Request: { "username": "admin", "password": "password", "captcha": "验证码" } Response: { "code": 200, "data": { "token": "jwt_token", "user": { "id": 1, "username": "admin", "role": "super_admin", "permissions": ["*"] } } } POST /api/admin/logout GET /api/admin/profile PUT /api/admin/profile POST /api/admin/change-password ``` #### 4.2.2 项目管理 ``` POST /api/admin/projects Request: { "name": "我的软件", "description": "软件描述", "maxDevices": 1, "autoUpdate": true } Response: { "code": 200, "data": { "id": 1, "projectId": "PROJ_001", -- 自动生成 "projectKey": "abcd1234...", -- 自动生成 "projectSecret": "secret123..." -- 自动生成(仅显示一次) } } GET /api/admin/projects GET /api/admin/projects/{id} PUT /api/admin/projects/{id} DELETE /api/admin/projects/{id} GET /api/admin/projects/{id}/stats GET /api/admin/projects/{id}/docs PUT /api/admin/projects/{id}/docs # 价格管理 GET /api/admin/projects/{id}/pricing Response: { "code": 200, "data": [ { "id": 1, "cardType": "day", "durationDays": 1, "originalPrice": 1.00, "isEnabled": true }, { "id": 2, "cardType": "week", "durationDays": 7, "originalPrice": 5.00, "isEnabled": true }, { "id": 3, "cardType": "month", "durationDays": 30, "originalPrice": 15.00, "isEnabled": true }, { "id": 4, "cardType": "year", "durationDays": 365, "originalPrice": 100.00, "isEnabled": true }, { "id": 5, "cardType": "lifetime", "durationDays": 36500, "originalPrice": 299.00, "isEnabled": true } ] } POST /api/admin/projects/{id}/pricing Request: { "cardType": "month", "durationDays": 30, "originalPrice": 15.00 } PUT /api/admin/projects/{id}/pricing/{priceId} Request: { "originalPrice": 20.00, "isEnabled": true } DELETE /api/admin/projects/{id}/pricing/{priceId} # 版本管理 GET /api/admin/projects/{id}/versions Response: { "code": 200, "data": [ { "id": 1, "version": "1.2.0", "fileSize": 52428800, "isStable": true, "isForceUpdate": false, "publishedAt": "2025-12-20T10:00:00Z" }, { "id": 2, "version": "1.1.0", "fileSize": 51200000, "isStable": true, "isForceUpdate": false, "publishedAt": "2025-12-10T10:00:00Z" } ] } POST /api/admin/projects/{id}/versions Request: { "version": "1.3.0", "file": "上传的文件", "changelog": "更新内容", "isForceUpdate": false, "isStable": true } Response: { "code": 200, "data": { "versionId": 3, "version": "1.3.0", "fileUrl": "https://cdn.example.com/software_v1.3.0.bin", "fileHash": "sha256:xxxxx", "encryptionKey": "RSA加密的密钥(仅首次返回)" } } PUT /api/admin/projects/{id}/versions/{versionId} Request: { "isForceUpdate": true, "isStable": false, "changelog": "更新内容" } DELETE /api/admin/projects/{id}/versions/{versionId} ``` #### 4.2.3 卡密管理 ``` # 批量生成卡密 POST /api/admin/cards/generate Request: { "projectId": "PROJ_001", "cardType": "month", -- day/week/month/year/lifetime "durationDays": 30, "quantity": 100, "note": "批次备注" } Response: { "code": 200, "data": { "batchId": "batch_xxx", "keys": [ "A3D7-K2P9-M8N1-Q4W6", "B4E8-L3X0-N9O2-P5Y7", ... ], "count": 100 } } # 卡密列表 GET /api/admin/cards?projectId=PROJ_001&status=active&page=1&pageSize=20 Response: { "code": 200, "data": { "total": 1000, "items": [ { "id": 1, "keyCode": "A3D7-K2P9-M8N1-Q4W6", "cardType": "month", "status": "active", "activateTime": "2025-12-01T10:00:00Z", "expireTime": "2025-12-31T23:59:59Z", "machineCode": "abc123...", "note": "备注", "createdAt": "2025-12-01T00:00:00Z" }, ... ] } } # 卡密详情 GET /api/admin/cards/{id} Response: { "code": 200, "data": { "id": 1, "keyCode": "A3D7-K2P9-M8N1-Q4W6", "project": { "id": 1, "name": "我的软件" }, "cardType": "month", "status": "active", "activateTime": "2025-12-01T10:00:00Z", "expireTime": "2025-12-31T23:59:59Z", "machineCode": "abc123...", "devices": [ { "deviceId": "abc123...", "deviceName": "DESKTOP-ABC", "ipAddress": "1.2.3.4", "lastHeartbeat": "2025-12-28T10:30:00Z", "isActive": true } ], "logs": [...], "createdAt": "2025-12-01T00:00:00Z" } } # 卡密操作 PUT /api/admin/cards/{id} -- 更新备注等 POST /api/admin/cards/{id}/ban -- 封禁 Request: { "reason": "违规使用" } POST /api/admin/cards/{id}/unban -- 解封 POST /api/admin/cards/{id}/extend -- 延长有效期 Request: { "days": 30 } POST /api/admin/cards/{id}/reset-device -- 重置设备绑定 DELETE /api/admin/cards/{id} -- 删除卡密 # 批量操作 POST /api/admin/cards/ban-batch -- 批量封禁 Request: { "ids": [1, 2, 3], "reason": "批量封禁" } POST /api/admin/cards/unban-batch -- 批量解封 DELETE /api/admin/cards/batch -- 批量删除 # 导出 GET /api/admin/cards/export?format=excel -- 导出卡密 # 批量导入(Excel/CSV) POST /api/admin/cards/import Content-Type: multipart/form-data Request: { "file": , "projectId": "PROJ_001" } Response: { "code": 200, "data": { "total": 100, "success": 95, "failed": 5, "errors": [ { "row": 3, "keyCode": "XXX", "reason": "格式错误" }, { "row": 7, "keyCode": "YYY", "reason": "已存在" } ] } } # 卡密日志 GET /api/admin/cards/{id}/logs -- 卡密操作日志 ``` #### 4.2.4 代理商管理 ``` # 代理商登录(独立登录入口) POST /api/agent/login Request: { "agentCode": "AGENT001", "password": "password" } Response: { "code": 200, "data": { "token": "jwt_token", "agent": { "id": 1, "agentCode": "AGENT001", "companyName": "某某科技公司", "balance": 5000.00, "discount": 80.00 }, "allowedProjects": [ { "projectId": "PROJ_001", "projectName": "项目A" }, { "projectId": "PROJ_002", "projectName": "项目B" } ] } } POST /api/agent/logout GET /api/agent/profile PUT /api/agent/profile POST /api/agent/change-password # 创建代理商(超管操作) POST /api/admin/agents Request: { "adminId": 5, -- 关联的管理员ID "agentCode": "AGENT001", "companyName": "某某科技公司", "contactPerson": "张三", "contactPhone": "13800138000", "contactEmail": "contact@example.com", "initialBalance": 1000.00, -- 初始额度 "discount": 80.00, -- 折扣(80=8折) "creditLimit": 500.00, -- 信用额度 "allowedProjects": ["PROJ_001", "PROJ_002"] } # 代理商列表 GET /api/admin/agents?status=active&page=1 Response: { "code": 200, "data": { "total": 10, "items": [ { "id": 1, "agentCode": "AGENT001", "companyName": "某某科技公司", "contactPerson": "张三", "contactPhone": "13800138000", "balance": 500.00, "discount": 80.00, "status": "active", "createdAt": "2025-12-01T00:00:00Z" } ] } } # 代理商详情 GET /api/admin/agents/{id} Response: { "code": 200, "data": { "id": 1, "agentCode": "AGENT001", "companyName": "某某科技公司", "balance": 500.00, "discount": 80.00, "stats": { "totalCards": 1000, "activeCards": 800, "totalRevenue": 50000.00 }, "transactions": [...] } } # 额度管理 POST /api/admin/agents/{id}/recharge -- 充值 Request: { "amount": 1000.00, "remark": "充值" } POST /api/admin/agents/{id}/deduct -- 扣款 Request: { "amount": 100.00, "remark": "扣款" } GET /api/admin/agents/{id}/transactions -- 额度流水 # 代理商操作 PUT /api/admin/agents/{id} POST /api/admin/agents/{id}/disable POST /api/admin/agents/{id}/enable DELETE /api/admin/agents/{id} ``` #### 4.2.5 设备管理 ``` GET /api/admin/devices?projectId=PROJ_001&isActive=true DELETE /api/admin/devices/{id} -- 解绑设备 POST /api/admin/devices/{id}/kick -- 强制下线 ``` #### 4.2.6 统计报表 ``` GET /api/admin/stats/dashboard Response: { "code": 200, "data": { "overview": { "totalProjects": 10, "totalCards": 10000, "activeCards": 6000, "activeDevices": 4500, "todayRevenue": 5000.00, "monthRevenue": 150000.00 }, "trend": { "dates": ["2025-12-01", "2025-12-02", ...], "activeUsers": [100, 120, ...], "newUsers": [10, 15, ...], "revenue": [500, 600, ...] }, "projectDistribution": [ { "project": "PROJ_001", "count": 3000 }, { "project": "PROJ_002", "count": 2000 } ] } } GET /api/admin/stats/projects GET /api/admin/stats/agents GET /api/admin/stats/logs GET /api/admin/stats/export ``` #### 4.2.7 日志审计 ``` GET /api/admin/logs?action=login&page=1 GET /api/admin/logs/{id} ``` #### 4.2.8 系统设置 ``` GET /api/admin/settings PUT /api/admin/settings GET /api/admin/admins POST /api/admin/admins PUT /api/admin/admins/{id} DELETE /api/admin/admins/{id} ``` --- ## 五、安全方案(防破解) > **⚠️ 重要认知:客户端防护的局限性** > > 任何运行在用户机器上的代码,理论上都可以被破解。客户端防护(混淆、加壳、反调试)只能**提高破解成本**,不能阻止所有破解行为。 > > **核心防线是服务端验证**: > - 关键功能必须在服务端完成验证后才能使用 > - 心跳机制确保持续在线验证 > - 风控引擎检测异常行为 > > **合理预期**: > - ✅ 防止大规模盗版分发 > - ✅ 让破解成本高于软件价格 > - ✅ 通过服务端及时封禁破解版 > - ❌ 无法阻止个别技术高手的破解 ### 5.1 多层防护体系 ``` ┌─────────────────────────────────────────────────────────────┐ │ 客户端防护层 │ │ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │ │ │ 代码混淆 │ │ 加壳保护 │ │ 反调试检测 │ │ │ │ (ConfuserEx) │ │ (Themida/ │ │ (Debugger/ │ │ │ │ │ │ VMP可选) │ │ VM检测) │ │ │ └──────────────┘ └──────────────┘ └──────────────┘ │ │ ┌──────────────┐ ┌──────────────┐ │ │ │ 完整性校验 │ │ 硬件绑定 │ │ │ │ (文件Hash) │ │ (机器指纹) │ │ │ └──────────────┘ └──────────────┘ │ └─────────────────────────────────────────────────────────────┘ │ ┌─────────────────────────────────────────────────────────────┐ │ 通信防护层 │ │ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │ │ │ HTTPS │ │ 请求签名 │ │ 时间戳验证 │ │ │ │ TLS 1.3 │ │ HMAC-SHA256 │ │ 防重放攻击 │ │ │ └──────────────┘ └──────────────┘ └──────────────┘ │ └─────────────────────────────────────────────────────────────┘ │ ┌─────────────────────────────────────────────────────────────┐ │ 服务端防护层 │ │ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │ │ │ 机器码绑定 │ │ 频率限制 │ │ 风控引擎 │ │ │ │ (硬件指纹) │ │ (Rate Limit)│ │ (异常检测) │ │ │ └──────────────┘ └──────────────┘ └──────────────┘ │ └─────────────────────────────────────────────────────────────┘ ``` ### 5.2 客户端防护代码示例 #### 1) 机器码生成(硬件指纹) ```csharp public static class MachineCode { public static string GetDeviceId() { var factors = new List { GetCpuId(), // CPU 处理器信息 GetBoardSerial(), // 主板序列号 GetDiskSerial(), // 硬盘序列号 GetMacAddress() // MAC 地址(取第一个非虚拟网卡) }; var combined = string.Join("|", factors.Where(x => !string.IsNullOrEmpty(x))); using var sha = SHA256.Create(); var hash = sha.ComputeHash(Encoding.UTF8.GetBytes(combined)); return Convert.ToHexString(hash).ToLower(); } private static string GetCpuId() { var cpuInfo = string.Empty; try { using var searcher = new ManagementObjectSearcher("SELECT ProcessorId FROM Win32_Processor"); foreach (var obj in searcher.Get()) { cpuInfo = obj["ProcessorId"]?.ToString(); break; } } catch { /* 忽略错误 */ } return cpuInfo ?? "unknown_cpu"; } // ... 其他硬件信息获取方法 } ``` #### 2) 请求签名 ```csharp public static class RequestSigner { public static string Sign(string projectId, string deviceId, long timestamp, string secret) { var payload = $"{projectId}|{deviceId}|{timestamp}"; using var hmac = new HMACSHA256(Encoding.UTF8.GetBytes(secret)); var hash = hmac.ComputeHash(Encoding.UTF8.GetBytes(payload)); return Convert.ToHexString(hash).ToLower(); } public static bool Verify(string projectId, string deviceId, long timestamp, string signature, string secret) { // 时间戳验证(5分钟有效期) var now = DateTimeOffset.UtcNow.ToUnixTimeSeconds(); if (Math.Abs(now - timestamp) > 300) return false; var expected = Sign(projectId, deviceId, timestamp, secret); return signature.Equals(expected, StringComparison.OrdinalIgnoreCase); } } ``` #### 3) 防篡改检测 ```csharp public static class IntegrityChecker { private static readonly string ExpectedHash = "嵌入的正确Hash值"; public static bool Verify() { // 1. 文件完整性校验 var currentHash = ComputeFileHash(Assembly.GetExecutingAssembly().Location); if (!currentHash.Equals(ExpectedHash, StringComparison.OrdinalIgnoreCase)) return false; // 2. 调试器检测 if (IsDebuggerAttached()) return false; // 3. 虚拟机检测(可选) if (IsRunningInVM()) Environment.Exit(0); return true; } private static bool IsDebuggerAttached() { return Debugger.IsAttached || CheckRemoteDebuggerPresent() || IsDebuggerPresentAPI(); } private static bool IsRunningInVM() { // 检测常见虚拟机特征 var vmKeys = new[] { "VBOX", "VMWARE", "QEMU", "VIRTUAL" }; try { using var searcher = new ManagementObjectSearcher("SELECT * FROM Win32_ComputerSystem"); foreach (var obj in searcher.Get()) { var manufacturer = obj["Manufacturer"]?.ToString()?.ToUpper() ?? ""; var model = obj["Model"]?.ToString()?.ToUpper() ?? ""; return vmKeys.Any(k => manufacturer.Contains(k) || model.Contains(k)); } } catch { } return false; } } ``` #### 4) 心跳机制 ```csharp public class HeartbeatService : BackgroundService { private readonly HttpClient _httpClient; private readonly string _accessToken; private readonly string _deviceId; protected override async Task ExecuteAsync(CancellationToken stoppingToken) { while (!stopping.IsCancellationRequested) { try { var response = await SendHeartbeat(stoppingToken); if (!response.Valid) { // 卡密失效,退出软件 MessageBox.Show("授权已失效,软件将退出"); Environment.Exit(0); } } catch { // 网络错误,允许短暂失败 } await Task.Delay(60000, stoppingToken); // 60秒心跳 } } private async Task SendHeartbeat(CancellationToken ct) { var timestamp = DateTimeOffset.UtcNow.ToUnixTimeSeconds(); var signature = RequestSigner.Sign(_projectId, _deviceId, timestamp, _secret); var payload = new { accessToken = _accessToken, deviceId = _deviceId, timestamp, signature }; var response = await _httpClient.PostAsJsonAsync("/api/auth/heartbeat", payload, ct); return await response.Content.ReadFromJsonAsync(ct); } } ``` ### 5.3 服务端风控规则 **基础风控规则:** | 风控规则 | 触发条件 | 处理措施 | |---------|---------|---------| | 异地登录 | 同一卡密1小时内IP跨省/跨运营商 | 要求重新验证,发送告警 | | 频繁请求 | 单IP 1分钟 > 100次请求 | 临时封禁5分钟 | | 机器码变更 | 已绑定卡密请求新机器码 | 拒绝并记录,需管理员解绑 | | 多机登录 | 同一卡密同时 > 最大设备数 | 踢出最早的设备 | | 心跳超时 | 连续3分钟无心跳 | 标记离线,下次拒绝 | | 批量注册 | 同一设备24小时内尝试 > 10个卡密 | 拉黑设备ID | **高级风控规则(推荐):** | 风控规则 | 触发条件 | 处理措施 | |---------|---------|---------| | 卡密爆破 | 同一IP 1小时内尝试 > 100个不同卡密 | 拉黑IP 24小时 | | 自动化检测 | 请求间隔完全均匀(如每60.00秒) | 标记可疑,增加验证频率 | | 设备指纹伪造 | 同一设备ID硬件参数变化 | 拒绝并告警 | | 代理IP检测 | IP属于已知代理池/数据中心 | 增加验证难度(验证码) | | 异常时段 | 凌晨2-6点批量操作 | 需要额外验证 | | 卡密共享 | 同一卡密24小时内 > 5个不同IP登录 | 自动封禁,需申诉 | **风控规则实现示例:** ```csharp public class RiskControl { private readonly IRedisCache _cache; public async Task CheckAsync(VerifyRequest request) { var rules = new List>> { () => CheckRateLimit(request.IpAddress), () => CheckDeviceLimit(request.KeyCode, request.DeviceId), () => CheckLocationChange(request.KeyCode, request.IpAddress), () => CheckBruteForce(request.IpAddress), }; foreach (var rule in rules) { var result = await rule(); if (!result.Passed) return result; } return RiskResult.Pass(); } private async Task CheckRateLimit(string ip) { var key = $"ratelimit:ip:{ip}"; var count = await _cache.IncrAsync(key); if (count == 1) await _cache.ExpireAsync(key, TimeSpan.FromMinutes(1)); if (count > 100) return RiskResult.Block("rate_limit_exceeded", "请求过于频繁,请稍后重试"); return RiskResult.Pass(); } } ``` ### 5.4 已知攻击与应对 | 攻击手段 | 描述 | 应对措施 | |---------|------|---------| | 破解验证逻辑 | 修改跳过验证的 patch | 代码混淆 + 关键逻辑服务端验证 + 心跳机制 | | 抓包重放 | 录制请求后重放 | 时间戳 + Nonce + 签名 | | 注入 DLL | Hook 关键函数 | 完整性校验 + 反调试 | | 虚拟机调试 | 在虚拟环境中分析 | 反虚拟机检测 | | 中间人攻击 | 代理篡改响应 | 证书固定 + 响应签名验证 | | 内存补丁 | 直接修改内存 | 关键数据多次读取 + 服务端校验 | | 脱壳爆破 | 去除加壳保护 | 多层保护 + Native AOT 编译 | ### 5.5 软件加密分发详细流程(方案2+方案3结合) ``` ┌─────────────────────────────────────────────────────────────────┐ │ 软件加密与分发完整流程 │ ├─────────────────────────────────────────────────────────────────┤ │ │ │ 【服务端 - 软件上传与加密】 │ │ │ │ ┌─────────────────────────────────────────────────────────┐ │ │ │ 1. 管理员上传原始软件包 │ │ │ │ POST /api/admin/projects/{id}/versions │ │ │ │ file: software.exe │ │ │ └─────────────────────────────────────────────────────────┘ │ │ │ │ │ ▼ │ │ ┌─────────────────────────────────────────────────────────┐ │ │ │ 2. 服务端加密处理 │ │ │ │ │ │ │ │ a) 生成随机 AES-256-GCM 密钥 │ │ │ │ b) 用 AES 密钥加密软件文件 │ │ │ │ c) 用客户端 RSA 公钥加密 AES 密钥 │ │ │ │ d) 计算 SHA256 哈希 │ │ │ │ e) 上传加密文件到 CDN/对象存储 │ │ │ │ │ │ │ │ 存储记录: │ │ │ │ - SoftwareVersions 表: │ │ │ │ FileUrl = CDN地址 │ │ │ │ FileHash = SHA256值 │ │ │ │ EncryptionKey = RSA加密后的AES密钥 │ │ │ └─────────────────────────────────────────────────────────┘ │ │ │ │ 【客户端 - 验证与下载】 │ │ │ │ ┌─────────────────────────────────────────────────────────┐ │ │ │ 3. 启动器卡密验证 │ │ │ │ POST /api/auth/verify │ │ │ │ │ │ │ │ 返回数据: │ │ │ │ { │ │ │ │ "downloadUrl": "https://cdn.../v1.2.0.bin", │ │ │ │ "fileHash": "sha256:xxxxx", │ │ │ │ "version": "1.2.0", │ │ │ │ "accessToken": "jwt_token" │ │ │ │ } │ │ │ └─────────────────────────────────────────────────────────┘ │ │ │ │ │ ▼ │ │ ┌─────────────────────────────────────────────────────────┐ │ │ │ 4. 下载加密软件包 │ │ │ │ GET /api/software/download?token=xxx&version=1.2.0 │ │ │ │ │ │ │ │ 响应头: │ │ │ │ X-File-Hash: sha256校验值 │ │ │ │ X-File-Size: 文件大小 │ │ │ │ X-Encryption-Key: RSA加密的AES密钥(Base64) │ │ │ │ X-Encryption-Nonce: 随机 nonce │ │ │ │ │ │ │ │ Response: 二进制加密数据 │ │ │ └─────────────────────────────────────────────────────────┘ │ │ │ │ │ ▼ │ │ ┌─────────────────────────────────────────────────────────┐ │ │ │ 5. 客户端解密 │ │ │ │ │ │ │ │ a) 用内置 RSA 私钥解密得到 AES 密钥 │ │ │ │ b) 用 AES 密钥解密软件数据 │ │ │ │ c) 验证 SHA256 哈希 │ │ │ │ d) 保存到临时文件或内存加载 │ │ │ └─────────────────────────────────────────────────────────┘ │ │ │ │ │ ▼ │ │ ┌─────────────────────────────────────────────────────────┐ │ │ │ 6. 启动主程序 │ │ │ │ │ │ │ │ Process.Start(tempFile) │ │ │ │ 或 Assembly.Load(decryptedBytes) │ │ │ └─────────────────────────────────────────────────────────┘ │ │ │ │ │ ▼ │ │ ┌─────────────────────────────────────────────────────────┐ │ │ │ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ │ │ │ │ 7. 主程序自验证(SDK,双重保险) │ │ │ │ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ │ │ │ │ │ │ │ │ - 向服务器发送自验证请求 │ │ │ │ - 确认卡密仍有效 │ │ │ │ - 启动心跳线程(每 30-60 秒) │ │ │ │ - 心跳失败 → 自动退出 │ │ │ └─────────────────────────────────────────────────────────┘ │ │ │ └─────────────────────────────────────────────────────────────────┘ ``` ### 5.6 服务端加密实现示例 ```csharp public class SoftwareEncryptionService { private readonly IStorageService _storage; private readonly RSA _clientRsa; // 客户端公钥 public async Task UploadAndEncryptAsync( string projectId, IFormFile file, string version) { // 1. 生成随机 AES 密钥 var aesKey = RandomNumberGenerator.GetBytes(32); var nonce = RandomNumberGenerator.GetBytes(12); // 2. 读取并加密文件 using var input = file.OpenReadStream(); using var output = new MemoryStream(); using var aes = new AesGcm(aesKey); var fileData = await.ReadAllBytesAsync(input); var encryptedData = new byte[fileData.Length]; var tag = new byte[16]; aes.Encrypt(nonce, fileData, 0, fileData.Length, encryptedData, tag); // 3. 组合 nonce + tag + ciphertext var finalData = new byte[nonce.Length + tag.Length + encryptedData.Length]; Buffer.BlockCopy(nonce, 0, finalData, 0, nonce.Length); Buffer.BlockCopy(tag, 0, finalData, nonce.Length, tag.Length); Buffer.BlockCopy(encryptedData, 0, finalData, nonce.Length + tag.Length, encryptedData.Length); // 4. 上传到存储 var filename = $"{projectId}_{version}.bin"; var url = await _storage.UploadAsync(filename, finalData); // 5. 用客户端 RSA 公钥加密 AES 密钥 var encryptedKey = _clientRsa.Encrypt(aesKey, RSAEncryptionPadding.OaepSHA256); // 6. 计算哈希 var hash = SHA256.HashData(finalData); // 7. 保存到数据库 return new SoftwareVersion { ProjectId = projectId, Version = version, FileUrl = url, FileSize = fileData.Length, FileHash = Convert.ToHexString(hash), EncryptionKey = Convert.ToBase64String(encryptedKey), PublishedAt = DateTime.UtcNow }; } } ``` ### 5.7 客户端解密实现示例 ```csharp public class SoftwareDownloader { private readonly RSA _privateKey; // 内置的客户端私钥 public async Task DownloadAndDecryptAsync( string downloadUrl, string encryptedKeyB64, string expectedHash, string tempPath) { // 1. 下载加密文件 using var client = new HttpClient(); var encryptedData = await client.GetByteArrayAsync(downloadUrl); // 2. 解密 AES 密钥 var encryptedKey = Convert.FromBase64String(encryptedKeyB64); var aesKey = _privateKey.Decrypt(encryptedKey, RSAEncryptionPadding.OaepSHA256); // 3. 解析数据:nonce(12) + tag(16) + ciphertext var nonce = encryptedData[..12]; var tag = encryptedData[12..28]; var ciphertext = encryptedData[28..]; // 4. AES-GCM 解密 using var aes = new AesGcm(aesKey); var decryptedData = new byte[ciphertext.Length]; aes.Decrypt(nonce, ciphertext, tag, decryptedData); // 5. 验证哈希 var actualHash = Convert.ToHexString(SHA256.HashData(encryptedData)); if (!actualHash.Equals(expectedHash, StringComparison.OrdinalIgnoreCase)) throw new Exception("文件完整性校验失败"); // 6. 写入临时文件 var tempFile = Path.Combine(tempPath, $"app_{Guid.NewGuid()}.exe"); await File.WriteAllBytesAsync(tempFile, decryptedData); return tempFile; } } ``` ### 5.8 防杀软误报处理 #### 报毒原因分析 | 类型 | 说明 | 示例 | |------|------|------| | **代码特征** | 特定代码序列与已知恶意软件相似 | 混淆器特征码 | | **行为特征** | 可疑行为模式 | 注入、反调试、隐藏窗口 | | **启发式** | AI 识别出可疑模式 | 加壳、加密、网络通信 | | **声誉检测** | 文件签名未知/新文件 | 未签名的二进制 | #### 判断与处理流程 ``` ┌─────────────────────────────────────────────────────────────┐ │ 杀软误报处理流程 │ ├─────────────────────────────────────────────────────────────┤ │ │ │ Step 1: 检查报毒类型 │ │ ┌─────────────────────────────────────────────────────────┐ │ │ │ 上传到 VirusTotal (https://www.virustotal.com/) │ │ │ │ 查看杀软检测情况和报毒类型 │ │ │ └─────────────────────────────────────────────────────────┘ │ │ │ │ │ ┌────────────────┬────────────────┬──────────────┐ │ │ ▼ ▼ ▼ │ │ │ 【仅哈希检测】 【代码特征】 【行为特征】 │ │ │ 重编译即可 需要改代码 需要改代码 │ │ │ │ │ Step 2: 尝试简单修复(仅哈希检测有效) │ │ ┌─────────────────────────────────────────────────────────┐ │ │ │ • 修改版本号 │ │ │ │ • 重新编译 │ │ │ │ • 更换时间戳 │ │ │ │ • 调整编译选项 │ │ │ └─────────────────────────────────────────────────────────┘ │ │ │ │ │ ▼ │ │ Step 3: 代码调整(如简单修复无效) │ │ ┌─────────────────────────────────────────────────────────┐ │ │ │ 【去除触发特征】 │ │ │ │ • 去掉或简化反调试代码 │ │ │ │ • 去掉注入、Hook 等敏感操作 │ │ │ │ • 减少混淆强度或更换混淆工具 │ │ │ │ • 正常的窗口行为,不隐藏主窗口 │ │ │ │ │ │ │ │ 【加壳相关】 │ │ │ │ • 更换加壳工具或参数 │ │ │ │ • 尝试不加壳,仅用混淆 │ │ │ └─────────────────────────────────────────────────────────┘ │ │ │ │ │ ▼ │ │ Step 4: 申请白名单(长期方案) │ │ ┌─────────────────────────────────────────────────────────┐ │ │ │ • 购买代码签名证书($150-600/年) │ │ │ │ - DigiCert: $400-600/年 │ │ │ │ - Sectigo: $150-200/年 │ │ │ │ - 国内CA: ¥500-1500/年 │ │ │ │ • 向杀软厂商申请白名单 │ │ │ │ • 提供软件说明和证明 │ │ │ └─────────────────────────────────────────────────────────┘ │ │ │ └─────────────────────────────────────────────────────────────┘ ``` #### 快速判断表 | 现象 | 原因 | 解决方法 | |------|------|---------| | 仅 1-2 个杀软报毒,其他正常 | 哈希碰撞/误报 | 重编译即可 | | 多个杀软报「Heur/Trojan」 | 启发式检测 | 去掉反调试/反虚拟机代码 | | 多个杀软报「Packed」 | 加壳检测 | 更换加壳工具或去掉加壳 | | 全部报毒 | 代码行为可疑 | 检查是否有注入/Hook等操作 | | 报「Confuser」特征 | 混淆器特征 | 更换混淆工具或调整参数 | | 报「Themida」特征 | 加壳工具特征 | 更换加壳工具或参数 | #### 预防措施(开发阶段) ``` ┌─────────────────────────────────────────────────────────────┐ │ 开发阶段预防措施 │ ├─────────────────────────────────────────────────────────────┤ │ ✅ 避免使用被黑的混淆器/加壳工具 │ │ ✅ 减少反调试、反虚拟机检测(或设为可选) │ │ ✅ 不要注入其他进程 │ │ ✅ 正常的窗口行为,不隐藏主窗口 │ │ ✅ 使用代码签名证书 │ │ ✅ 定期在 VirusTotal 上测试 │ └─────────────────────────────────────────────────────────────┘ ``` #### 总结 - 重编译可能解决问题(10-20% 情况) - 大部分情况需要调整代码(去掉反调试等) - 长期方案是购买代码签名证书 --- ## 六、卡密生成算法 ### 6.1 卡密格式 ``` 格式: XXXX-XXXX-XXXX-XXXX (4组,每组4字符) 示例: A3D7-K2P9-M8N1-Q4W6 编码方式: Base32(避免混淆字符) 排除字符: 0/O, 1/I/l 字符集: 23456789ABCDEFGHJKMNPQRSTUVWXYZ ``` ### 6.2 生成逻辑 ```csharp public static class CardKeyGenerator { private static readonly string Base32Chars = "23456789ABCDEFGHJKMNPQRSTUVWXYZ"; public static string Generate(string projectId, CardType type, int durationDays) { // 1. 生成随机数据 var randomBytes = new byte[12]; RandomNumberGenerator.Fill(randomBytes); // 2. 添加类型和时长信息 var payload = new byte[16]; Array.Copy(randomBytes, 0, payload, 0, 12); payload[12] = (byte)type; Array.Copy(BitConverter.GetBytes(durationDays), 0, payload, 13, 2); // 3. 计算校验码 var crc = Crc32.Compute(payload); var checksum = BitConverter.GetBytes(crc).Take(2).ToArray(); // 4. Base32 编码 var fullPayload = payload.Concat(checksum).ToArray(); var encoded = Base32Encode(fullPayload); // 5. 格式化输出 return FormatKey(encoded); } public static bool Validate(string keyCode) { // 1. 格式校验 if (!Regex.IsMatch(keyCode, @"^[2-9A-HJ-NP-Z]{4}-[2-9A-HJ-NP-Z]{4}-[2-9A-HJ-NP-Z]{4}-[2-9A-HJ-NP-Z]{4}$")) return false; // 2. 校验码验证 var raw = keyCode.Replace("-", ""); var payload = Base32Decode(raw); var receivedCrc = BitConverter.ToUInt16(payload.AsSpan(^2)); var computedCrc = Crc32.Compute(payload[..(^2)]); return receivedCrc == computedCrc; } private static string FormatKey(string encoded) { var parts = new List(); for (int i = 0; i < encoded.Length; i += 4) { parts.Add(encoded.Substring(i, Math.Min(4, encoded.Length - i))); } return string.Join("-", parts.Take(4)); } } ``` --- ## 七、技术栈 ### 7.1 后端 | 技术 | 版本 | 用途 | |------|------|------| | ASP.NET Core | 8.0 | Web API 框架 | | PostgreSQL | 16+ | 数据库 | | Redis | 7+ | 缓存 + 分布式锁 | | JWT | - | Token 生成 | | Serilog | - | 日志记录 | | Polly | - | 重试策略 | ### 7.2 前端(管理后台) | 技术 | 版本 | 用途 | |------|------|------| | Vue | 3.4+ | 前端框架 | | Element Plus | Latest | UI 组件库 | | Pinia | Latest | 状态管理 | | Axios | Latest | HTTP 请求 | | ECharts | 5+ | 数据可视化 | ### 7.3 客户端 | 技术 | 版本 | 用途 | |------|------|------| | .NET | 8.0 | 运行时 | | WPF / WinUI 3 | - | UI 框架 | | RestSharp | - | HTTP 客户端 | | Microsoft.Extensions.Hosting | - | 后台服务(心跳) | ### 7.4 安全工具 | 工具 | 类型 | 用途 | |------|------|------| | ConfuserEx | 开源免费 | 代码混淆 | | Obfuscar | 开源免费 | 另一种混淆选择 | | Themida | 商业 $ | 强力加壳,反调试 | | VMProtect | 商业 $ | 虚拟化保护 | | dnSpy | 免费测试 | 反编译测试(验证防护效果) | --- ## 八、部署方案(单服务器) > 本方案针对小型项目,采用单服务器 Docker Compose 部署,简单可靠。 ### 8.1 单服务器架构 ``` ┌─────────────────────────────────────────────────────────┐ │ 单服务器 (4核8G) │ │ │ │ ┌─────────────────────────────────────────────────────┐ │ │ │ Nginx │ │ │ │ (反向代理 + SSL) │ │ │ │ :80 :443 │ │ │ └───────────────────────┬─────────────────────────────┘ │ │ │ │ │ ┌───────────────────────┴─────────────────────────────┐ │ │ │ Docker Compose │ │ │ │ ┌──────────┐ ┌──────────┐ ┌──────────┐ │ │ │ │ │ API │ │PostgreSQL│ │ Redis │ │ │ │ │ │ :39256 │ │ :5432 │ │ :6379 │ │ │ │ │ └──────────┘ └──────────┘ └──────────┘ │ │ │ └─────────────────────────────────────────────────────┘ │ │ │ │ ┌─────────────────────────────────────────────────────┐ │ │ │ 本地文件存储 │ │ │ │ /data/uploads (软件包) │ │ │ │ /data/backups (数据库备份) │ │ │ └─────────────────────────────────────────────────────┘ │ └─────────────────────────────────────────────────────────┘ ``` ### 8.2 服务器要求 | 配置 | 最低配置 | 推荐配置 | 说明 | |------|---------|---------|------| | CPU | 2核 | 4核 | 并发100以内2核足够 | | 内存 | 4GB | 8GB | PostgreSQL需要较多内存 | | 硬盘 | 40GB SSD | 100GB SSD | 预留日志和备份空间 | | 带宽 | 5Mbps | 10Mbps | 软件下载需要带宽 | ### 8.3 Docker Compose 完整配置 ```yaml # docker-compose.yml version: '3.8' services: api: build: ./backend container_name: license-api restart: always environment: - ASPNETCORE_ENVIRONMENT=Production - ConnectionStrings__DefaultConnection=Host=db;Port=5432;Database=license;Username=license;Password=${DB_PASSWORD} - Redis__ConnectionString=redis:6379 - Jwt__Secret=${JWT_SECRET} - Jwt__Issuer=license-system - Jwt__ExpireMinutes=1440 volumes: - ./data/uploads:/app/uploads - ./data/logs:/app/logs ports: - "127.0.0.1:39256:39256" depends_on: db: condition: service_healthy redis: condition: service_started healthcheck: test: ["CMD", "curl", "-f", "http://localhost:39256/health/live"] interval: 30s timeout: 10s retries: 3 db: image: postgres:16-alpine container_name: license-db restart: always environment: POSTGRES_DB: license POSTGRES_USER: license POSTGRES_PASSWORD: ${DB_PASSWORD} volumes: - ./data/postgres:/var/lib/postgresql/data - ./scripts/init.sql:/docker-entrypoint-initdb.d/init.sql ports: - "127.0.0.1:5432:5432" healthcheck: test: ["CMD-SHELL", "pg_isready -U license"] interval: 10s timeout: 5s retries: 5 redis: image: redis:7-alpine container_name: license-redis restart: always command: redis-server --appendonly yes --maxmemory 256mb --maxmemory-policy allkeys-lru volumes: - ./data/redis:/data ports: - "127.0.0.1:6379:6379" volumes: postgres_data: redis_data: ``` ### 8.4 环境变量配置 ```bash # .env 文件(不要提交到Git) DB_PASSWORD=your_strong_password_here JWT_SECRET=your_jwt_secret_at_least_32_chars ADMIN_PASSWORD=initial_admin_password ``` ### 8.5 Nginx 配置 ```nginx # /etc/nginx/sites-available/license.conf server { listen 80; server_name api.yourdomain.com; return 301 https://$server_name$request_uri; } server { listen 443 ssl http2; server_name api.yourdomain.com; ssl_certificate /etc/letsencrypt/live/yourdomain.com/fullchain.pem; ssl_certificate_key /etc/letsencrypt/live/yourdomain.com/privkey.pem; ssl_protocols TLSv1.2 TLSv1.3; ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256; ssl_prefer_server_ciphers off; # 安全头 add_header X-Frame-Options "SAMEORIGIN" always; add_header X-Content-Type-Options "nosniff" always; add_header X-XSS-Protection "1; mode=block" always; # API代理 location /api/ { proxy_pass http://127.0.0.1:39256; proxy_http_version 1.1; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; # 超时设置 proxy_connect_timeout 60s; proxy_send_timeout 60s; proxy_read_timeout 60s; } # 健康检查 location /health/ { proxy_pass http://127.0.0.1:39256; access_log off; } # 软件下载(大文件) location /api/software/download { proxy_pass http://127.0.0.1:39256; proxy_read_timeout 300s; proxy_buffering off; } # 限流配置 limit_req_zone $binary_remote_addr zone=api:10m rate=10r/s; location /api/auth/ { limit_req zone=api burst=20 nodelay; proxy_pass http://127.0.0.1:39256; } } ``` ### 8.6 数据库备份脚本 ```bash #!/bin/bash # /opt/license/scripts/backup.sh BACKUP_DIR="/opt/license/data/backups" DATE=$(date +%Y%m%d_%H%M%S) KEEP_DAYS=7 # 创建备份目录 mkdir -p $BACKUP_DIR # 备份PostgreSQL docker exec license-db pg_dump -U license license | gzip > $BACKUP_DIR/db_$DATE.sql.gz # 备份Redis docker exec license-redis redis-cli BGSAVE sleep 5 cp /opt/license/data/redis/dump.rdb $BACKUP_DIR/redis_$DATE.rdb # 删除旧备份 find $BACKUP_DIR -type f -mtime +$KEEP_DAYS -delete echo "Backup completed: $DATE" ``` 添加定时任务: ```bash # crontab -e 0 3 * * * /opt/license/scripts/backup.sh >> /var/log/license-backup.log 2>&1 ``` ### 8.7 快速部署步骤 ```bash # 1. 安装Docker和Docker Compose curl -fsSL https://get.docker.com | sh apt install docker-compose-plugin # 2. 创建项目目录 mkdir -p /opt/license/{data,scripts} cd /opt/license # 3. 配置环境变量 cp .env.example .env nano .env # 修改密码 # 4. 启动服务 docker compose up -d # 5. 检查状态 docker compose ps docker compose logs -f api # 6. 配置Nginx和SSL apt install nginx certbot python3-certbot-nginx certbot --nginx -d api.yourdomain.com # 7. 访问测试 curl https://api.yourdomain.com/health/ready ``` --- ## 九、实施步骤(小项目工期估算) > **团队配置**:2-3人(1后端 + 1前端 + 0.5客户端) > **总工期**:8-10周(可根据功能优先级裁剪) ### Phase 1: 基础架构(2周) **第1周:后端框架** - [ ] 项目初始化(ASP.NET Core 8.0) - [ ] 数据库表结构搭建 + 迁移脚本 - [ ] 基础认证(JWT) - [ ] 卡密生成/验证服务 **第2周:核心API** - [ ] 项目管理 API - [ ] 卡密管理 API(CRUD + 批量生成) - [ ] 设备绑定 + 心跳验证 - [ ] Docker 环境配置 ### Phase 2: 管理后台(2-3周) **第3周:基础页面** - [ ] Vue3 项目初始化 + 路由 - [ ] 登录/权限模块 - [ ] 项目管理页面 - [ ] 卡密列表 + 生成页面 **第4周:功能完善** - [ ] 卡密详情 + 操作(封禁/解绑等) - [ ] 代理商管理(可选,如不需要可跳过) - [ ] 系统设置页面 **第5周(可选):增强功能** - [ ] 统计报表(ECharts) - [ ] 日志审计 - [ ] 批量导入/导出 ### Phase 3: 客户端启动器(1-2周) **第6周:基础功能** - [ ] WPF 界面(登录 + 主界面) - [ ] 卡密验证逻辑 - [ ] 机器码生成 - [ ] 软件下载与启动 **第7周(可选):增强** - [ ] 自动更新检测 - [ ] 心跳服务 - [ ] 离线缓存 ### Phase 4: 安全与测试(2周) **第8周:安全加固** - [ ] 请求签名验证 - [ ] 风控规则(限流 + 异常检测) - [ ] ConfuserEx 混淆配置 - [ ] 基础反调试 **第9周:测试与部署** - [ ] 功能测试(手动 + 自动化) - [ ] 压力测试(JMeter/K6) - [ ] 生产环境部署 - [ ] 监控配置 ### 工期对照表 | 功能模块 | MVP版本 | 完整版本 | 备注 | |---------|---------|---------|------| | 后端API | 2周 | 3周 | 核心必须 | | 管理后台 | 2周 | 3周 | 可先做基础版 | | 客户端 | 1周 | 2周 | 可后期迭代 | | 安全加固 | 1周 | 2周 | 服务端优先 | | 测试部署 | 1周 | 1周 | 不可省略 | | **总计** | **7周** | **11周** | - | ### MVP优先级建议 **必须有(MVP)**: 1. 卡密生成/验证/绑定 2. 基础管理后台(项目+卡密) 3. 简单客户端(登录+启动) 4. 基础安全(签名+限流) **可后期迭代**: 1. 代理商系统 2. 统计报表 3. 自动更新 4. 高级风控 --- ## 十、风险与局限 ### 10.1 防护的现实边界 **必须承认:** > 没有绝对安全的客户端防护。任何运行在用户机器上的代码,理论上都可以被破解。 **合理目标:** - 提高破解成本,让破解难度 > 软件价值 - 防止大规模盗版(允许个别破解案例存在) - 及时发现异常行为并封禁 - 通过服务端校验,让破解者需要持续维护 ### 10.2 安全建议 1. **持续更新** - 频繁更新客户端,让破解者跟不上 2. **服务端校验** - 核心逻辑放服务端,客户端只做展示 3. **在线验证** - 必须联网才能使用(牺牲体验换安全) 4. **法律手段** - 用户协议明确禁止逆向,配合法律追责 ### 10.3 成本对比 | 方案 | 成本 | 安全等级 | 建议 | |------|------|----------|------| | 无保护 | ¥0 | ⭐ | 不推荐 | | 开源混淆 | ¥0 | ⭐⭐ | 个人项目 | | 混淆+反调试 | ¥0 | ⭐⭐⭐ | 中小型项目 | | 商业加壳 | $100-500/年 | ⭐⭐⭐⭐ | 商业项目 | | 硬件狗 | $20+/个 | ⭐⭐⭐⭐⭐ | 高价值软件 | --- ## 十一、后续扩展 1. **多租户支持** - 允许第三方开发者注册使用 2. **代理商系统** - 支持多级代理分销 3. **卡密续费** - 到期后续费延长 4. **使用时长限制** - 按小时/天数计费 5. **移动端** - Android/iOS 授权验证 6. **Web 版** - 支持浏览器内使用(WebAssembly) 7. **数据分析** - 用户行为分析、转化漏斗 --- ## 十二、管理后台页面设计 ### 12.1 页面结构 ``` ┌─────────────────────────────────────────────────────────────────┐ │ 管理后台 │ ├─────────────────────────────────────────────────────────────────┤ │ ┌─────────┐ │ │ │ Logo │ 软件授权管理系统 管理员 ▼ │ │ └─────────┘ │ ├──┬──────────────────────────────────────────────────────────────┤ │ │ │ │ │ 📊 仪表盘 📁 项目 🔑 卡密 👥 代理 🖥️ 设备 │ │ │ │ │ ├──────────────────────────────────────────────────────────────┤ │ │ │ │ │ 【页面内容区】 │ │ │ │ │ │ │ │ │ │ │ │ │ │ └──────────────────────────────────────────────────────────────┘ └─────────────────────────────────────────────────────────────────┘ ``` ### 12.2 关键页面原型 #### 12.2.1 仪表盘页面 ``` ┌─────────────────────────────────────────────────────────────────┐ │ 📊 仪表盘 [刷新] │ ├─────────────────────────────────────────────────────────────────┤ │ │ │ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │ │ │ 总卡密数 │ │ 活跃卡密 │ │ 在线设备 │ │ │ │ 10,234 │ │ 6,789 │ │ 4,521 │ │ │ └─────────────┘ └─────────────┘ └─────────────┘ │ │ │ │ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │ │ │ 今日收入 │ │ 本月收入 │ │ 今日新增 │ │ │ │ ¥3,280 │ │ ¥89,500 │ │ 156 │ │ │ └─────────────┘ └─────────────┘ └─────────────┘ │ │ │ │ ┌─────────────────────────────────────────────────────────┐ │ │ │ 活跃用户趋势 │ │ │ │ ▁▂▃▅▆▇█▇▆▅▃▂▁ (ECharts 折线图) │ │ │ └─────────────────────────────────────────────────────────┘ │ │ │ │ ┌────────────────────┐ ┌──────────────────────────────────┐ │ │ │ 项目分布 │ │ 最近活动 │ │ │ │ (饼图) │ │ • 代理商A生成100张卡密 10:00 │ │ │ │ │ │ • 用户B激活卡密 09:58 │ │ │ │ │ │ • 代理商C充值¥5000 09:30 │ │ │ └────────────────────┘ └──────────────────────────────────┘ │ │ │ └─────────────────────────────────────────────────────────────────┘ ``` #### 12.2.2 卡密生成页面 ``` ┌─────────────────────────────────────────────────────────────────┐ │ 🔑 批量生成卡密 │ ├─────────────────────────────────────────────────────────────────┤ │ │ │ 项目: [下拉选择 ▼] (必填) │ │ 卡密类型: ○ 天卡 ○ 周卡 ● 月卡 ○ 年卡 ○ 永久 │ │ 有效期: [30] 天 │ │ 生成数量: [100] (1-10000) │ │ 备注: [_______________________________] │ │ │ │ 预计消耗额度:¥300.00 (当前余额:¥5,000.00) │ │ │ │ [取消] [生成卡密] │ │ │ └─────────────────────────────────────────────────────────────────┘ │ 生成后 ▼ ┌─────────────────────────────────────────────────────────────────┐ │ ✓ 生成成功! │ ├─────────────────────────────────────────────────────────────────┤ │ 已生成 100 张卡密 │ │ │ │ 批次ID:batch_20251228_001 │ │ 项目:我的软件 (PROJ_001) │ │ 类型:月卡 (30天) │ │ │ │ [复制全部] [导出TXT] [导出CSV] [导出Excel] [关闭] │ │ │ │ ┌─────────────────────────────────────────────────────────┐ │ │ │ A3D7-K2P9-M8N1-Q4W6 │ │ │ │ B4E8-L3X0-N9O2-P5Y7 │ │ │ │ C5F9-M4Y1-P0Q3-R6T8 │ │ │ │ ... (共100条) │ │ │ └─────────────────────────────────────────────────────────┘ │ │ │ └─────────────────────────────────────────────────────────────────┘ ``` #### 12.2.3 卡密列表页面 ``` ┌─────────────────────────────────────────────────────────────────┐ │ 🔑 卡密管理 │ ├─────────────────────────────────────────────────────────────────┤ │ 项目: [全部 ▼] 状态: [全部 ▼] 类型: [全部 ▼] │ │ 搜索: [__________________] [搜索] │ │ │ │ ☑ 批量操作: [封禁] [解封] [删除] [导出] │ │ │ │ ┌──┬────────────┬────┬──────┬────────┬────────┬──────┬────┐ │ │ │☑│ 卡密 │类型│ 状态 │ 激活时间│ 过期时间│ 备注 │操作│ │ │ ├──┼────────────┼────┼──────┼────────┼────────┼──────┼────┤ │ │ │☑│A3D7-K2P9.. │月卡│ ●活跃│12-01 │12-31 │ │详情│ │ │ │☑│B4E8-L3X0.. │月卡│ ○未用│ - │ - │ │编辑│ │ │ │☑│C5F9-M4Y1.. │月卡│ ✗封禁│12-01 │12-31 │违规 │解封│ │ │ │☑│... │... │ ... │ ... │ ... │ ... │... │ │ │ └──┴────────────┴────┴──────┴────────┴────────┴──────┴────┘ │ │ │ │ 共 1000 条 [< 1 2 3 4 5 ... 20 >] 每页 [20]▼ 条 │ │ │ └─────────────────────────────────────────────────────────────────┘ ``` #### 12.2.4 卡密详情页面 ``` ┌─────────────────────────────────────────────────────────────────┐ │ 🔑 卡密详情 [返回列表] │ ├─────────────────────────────────────────────────────────────────┤ │ │ │ 基本信息 │ │ ┌─────────────────────────────────────────────────────────┐ │ │ │ 卡密: A3D7-K2P9-M8N1-Q4W6 │ │ │ │ 项目: 我的项目 (PROJ_001) │ │ │ │ 类型: 月卡 (30天) │ │ │ │ 状态: ● 活跃 │ │ │ │ 激活时间: 2025-12-01 10:30:00 │ │ │ │ 过期时间: 2025-12-31 23:59:59 (剩余 3 天) │ │ │ │ 备注: 批次20251228 │ │ │ └─────────────────────────────────────────────────────────┘ │ │ │ │ 设备信息 │ │ ┌─────────────────────────────────────────────────────────┐ │ │ │ 设备ID: abc123def456... │ │ │ │ 设备名: DESKTOP-ABC123 │ │ │ │ 最后心跳: 2025-12-28 10:30:00 (● 在线) │ │ │ │ 登录IP: 1.2.3.4 (中国-广东-深圳) │ │ │ │ 首次登录: 2025-12-01 10:30:00 │ │ │ │ [强制下线] [解绑设备] │ │ │ └─────────────────────────────────────────────────────────┘ │ │ │ │ 操作日志 │ │ ┌─────────────────────────────────────────────────────────┐ │ │ │ 2025-12-28 10:30 心跳验证 系统 1.2.3.4 │ │ │ │ 2025-12-01 10:30 激活卡密 用户 1.2.3.4 │ │ │ │ 2025-11-28 00:00 生成卡密 管理员 admin │ │ │ └─────────────────────────────────────────────────────────┘ │ │ │ │ [封禁卡密] [延长30天] [修改备注] [删除] │ │ │ └─────────────────────────────────────────────────────────────────┘ ``` #### 12.2.5 项目管理页面 ``` ┌─────────────────────────────────────────────────────────────────┐ │ 📁 项目管理 [+ 新建项目] │ ├─────────────────────────────────────────────────────────────────┤ │ │ │ ┌─────────────────────────────────────────────────────────┐ │ │ │ 我的项目 v1.0 [编辑][删除] │ │ │ │ PROJ_001 | 活跃卡密: 1234 | 总卡密: 5000 │ │ │ │ -------------------------------------------------------- │ │ │ │ ProjectKey: abcd1234efgh5678 │ │ │ │ ProjectSecret: **** [显示/复制] │ │ │ │ 软件版本: v1.0.0 │ │ │ │ 软件地址: https://cdn.example.com/app.zip │ │ │ │ 最大设备数: 1 │ │ │ │ │ │ │ │ [查看统计] [管理卡密] [开发文档] [上传新版本] │ │ │ └─────────────────────────────────────────────────────────┘ │ │ │ │ ┌─────────────────────────────────────────────────────────┐ │ │ │ 另一个项目 v2.0 [编辑][删除] │ │ │ │ ... │ │ │ └─────────────────────────────────────────────────────────┘ │ │ │ └─────────────────────────────────────────────────────────────────┘ ``` #### 12.2.6 代理商管理页面 ``` ┌─────────────────────────────────────────────────────────────────┐ │ 👥 代理商管理 [+ 新建代理] │ ├─────────────────────────────────────────────────────────────────┤ │ │ │ ┌─────────────────────────────────────────────────────────┐ │ │ │ 某某科技公司 (AGENT001) [编辑][删除] │ │ │ │ -------------------------------------------------------- │ │ │ │ 联系人:张三 | 电话:13800138000 | 邮箱:... │ │ │ │ 余额:¥5,000.00 | 折扣:80% | 状态:● 活跃 │ │ │ │ -------------------------------------------------------- │ │ │ │ 统计:总卡密 1000 | 活跃 800 | 收入 ¥50,000 │ │ │ │ │ │ │ │ [充值] [查看明细] [销售统计] [额度流水] │ │ │ └─────────────────────────────────────────────────────────┘ │ │ │ └─────────────────────────────────────────────────────────────────┘ ``` #### 12.2.7 开发文档页面 ``` ┌─────────────────────────────────────────────────────────────────┐ │ 📖 开发文档 - 我的项目 [编辑文档] │ ├─────────────────────────────────────────────────────────────────┤ │ │ │ 目录 │ │ ├── 1. 快速开始 │ │ ├── 2. 项目配置 │ │ ├── 3. SDK 集成 │ │ ├── 4. API 接口 │ │ ├── 5. 代码示例 │ │ └── 6. 常见问题 │ │ │ │ ──────────────────────────────────────────────────────────── │ │ │ │ 1. 快速开始 │ │ │ │ 本文档将指导您如何在软件中集成授权验证功能。 │ │ │ │ 2. 项目配置 │ │ │ │ 您的项目信息如下: │ │ ┌─────────────────────────────────────────────────────────┐ │ │ │ ProjectId: PROJ_001 │ │ │ │ ProjectKey: abcd1234efgh5678 [复制] │ │ │ │ ProjectSecret: xyz999aaa888bbb777 [复制] │ │ │ │ API地址: https://api.example.com │ │ │ └─────────────────────────────────────────────────────────┘ │ │ │ │ ⚠️ 重要:ProjectSecret 仅在此显示一次,请妥善保管! │ │ │ │ 3. SDK 集成 │ │ │ │ 步骤1:安装 NuGet 包 │ │ ```bash │ │ Install-Package YourCompany.License.SDK │ │ ``` │ │ │ │ 步骤2:在 Program.cs 中初始化 │ │ ```csharp │ │ using YourCompany.License.SDK; │ │ │ │ │ var licenseClient = new LicenseClient(new LicenseConfig { │ │ ProjectId = "PROJ_001", │ │ ProjectKey = "abcd1234efgh5678", │ │ ProjectSecret = "xyz999aaa888bbb777", │ │ ApiEndpoint = "https://api.example.com" │ │ }); │ │ │ │ │ var result = await licenseClient.VerifyAsync(cardKey); │ │ if (!result.IsValid) { │ │ MessageBox.Show("卡密验证失败:" + result.Message); │ │ Application.Current.Shutdown(); │ │ } │ │ ``` │ │ │ │ [继续阅读下一页 →] │ │ │ └─────────────────────────────────────────────────────────────────┘ ``` ### 12.3 权限控制 | 角色 | 仪表盘 | 项目 | 卡密 | 代理 | 设备 | 日志 | 设置 | |------|:-----:|:----:|:----:|:----:|:----:|:----:|:----:| | 超级管理员 | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | | 管理员 | ✅ | ✅ | ✅ | ❌ | ✅ | ✅ | ❌ | | 代理商 | ✅ | 限 | 限 | ❌ | ✅ | 限 | ❌ | ### 12.4 超管/管理员后台 vs 代理商后台 ``` ┌─────────────────────────────────────────────────────────────────┐ │ 超管/管理员后台菜单 │ ├─────────────────────────────────────────────────────────────────┤ │ 📊 仪表盘 ✅ ──► 完整统计 │ │ 📁 项目管理 ✅ ──► 全部项目 │ │ 🔑 卡密管理 ✅ ──► 完整CRUD + 批量操作 │ │ 👥 代理商 ✅ ──► 创建/管理代理商(仅超管) │ │ 🖥️ 设备管理 ✅ ──► 所有设备 │ │ 📝 日志审计 ✅ ──► 完整日志 │ │ ⚙️ 系统设置 ✅ ──► 系统配置 │ └─────────────────────────────────────────────────────────────────┘ ┌─────────────────────────────────────────────────────────────────┐ │ 代理商后台菜单 │ ├─────────────────────────────────────────────────────────────────┤ │ 📊 仪表盘 ✅ ──► 仅显示自己的数据 │ │ 📁 项目管理 限 ──► 仅显示授权的项目 │ │ 🔑 卡密管理 限 ──► 仅能生成/管理自己生成的卡密 │ │ 👥 代理商 ❌ ──► 不可见 │ │ 🖥️ 设备管理 限 ──► 仅自己卡密的设备 │ │ 📝 日志审计 限 ──► 仅自己的操作日志 │ │ ⚙️ 系统设置 ❌ ──► 不可见 │ │ 💰 额度管理 ✅ ──► 查看余额、充值记录(代理商专属) │ └─────────────────────────────────────────────────────────────────┘ ``` ### 12.5 代理商后台页面 ``` ┌─────────────────────────────────────────────────────────────────┐ │ 💰 额度中心 余额: ¥5,000 │ ├─────────────────────────────────────────────────────────────────┤ │ │ │ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐ │ │ │ 当前余额 │ │ 本月消耗 │ │ 充值记录 │ │ │ │ ¥5,000.00 │ │ ¥3,000.00 │ │ 15 次 │ │ │ └─────────────────┘ └─────────────────┘ └─────────────────┘ │ │ │ │ ──────────────────────────────────────────────────────────── │ │ │ │ 💡 我的折扣:80%(8折) │ │ │ │ ┌─────────────────────────────────────────────────────────┐ │ │ │ 项目价格(折扣后) │ │ │ │ │ │ │ │ 项目A - 我的项目 │ │ │ │ 天卡 ¥0.80 | 周卡 ¥4.00 | 月卡 ¥12.00 │ │ │ │ 年卡 ¥80.00 | 永久卡 ¥239.20 │ │ │ │ │ │ │ │ 项目B - 另一个项目 │ │ │ │ 天卡 ¥1.00 | 周卡 ¥5.00 | 月卡 ¥15.00 │ │ │ │ │ │ │ └─────────────────────────────────────────────────────────┘ │ │ │ │ ──────────────────────────────────────────────────────────── │ │ │ │ 📊 充值/消费流水 │ │ ┌─────────────────────────────────────────────────────────┐ │ │ │ 2025-12-28 10:30 充值 +¥1000.00 余额 ¥5000.00 │ │ │ │ 2025-12-27 15:20 消费 -¥15.00 余额 ¥4000.00 │ │ │ │ 2025-12-25 09:00 充值 +¥5000.00 余额 ¥4015.00 │ │ │ │ ... │ │ │ │ │ │ │ │ [查看更多记录 →] │ │ │ └─────────────────────────────────────────────────────────┘ │ │ │ └─────────────────────────────────────────────────────────────────┘ ``` --- ## 十三、系统设置(前端配置驱动) ### 13.1 设置页面设计 ``` ┌─────────────────────────────────────────────────────────────────┐ │ ⚙️ 系统设置 [保存更改] │ ├─────────────────────────────────────────────────────────────────┤ │ │ │ ┌─────────────────────────────────────────────────────────┐ │ │ │ 🔧 功能开关 │ │ │ │ │ │ │ │ ┌─────────────────────────────────────────────────────┐ │ │ │ │ │ [✓] 启用心跳验证 说明:客户端需定期验证 │ │ │ │ │ │ [✓] 启用设备绑定 说明:单卡密绑定单设备 │ │ │ │ │ │ [✓] 启用自动更新 说明:客户端自动检测更新 │ │ │ │ │ │ [ ] 启用强制更新 说明:旧版本强制升级 │ │ │ │ │ │ [✓] 启用代理商系统 说明:允许代理商销售 │ │ │ │ │ │ [✓] 启用卡密续费 说明:用户可续费延长 │ │ │ │ │ │ [ ] 启用试用模式 说明:未激活可试用X天 │ │ │ │ │ └─────────────────────────────────────────────────────┘ │ │ │ └─────────────────────────────────────────────────────────┘ │ │ │ │ ┌─────────────────────────────────────────────────────────┐ │ │ │ 🔐 验证规则 │ │ │ │ │ │ │ │ 单卡密最大设备数: [1] (1-10) │ │ │ │ [ ] 允许多设备同时在线 │ │ │ │ [✓] 卡密需要激活 │ │ │ │ 过期类型: (•)激活后计算 ( )固定时间 │ │ │ └─────────────────────────────────────────────────────────┘ │ │ │ │ ┌─────────────────────────────────────────────────────────┐ │ │ │ 💓 心跳配置 │ │ │ │ │ │ │ │ [✓] 启用心跳验证 │ │ │ │ 心跳间隔: [60] 秒 (10-300) │ │ │ │ 心跳超时: [180] 秒 (30-600) │ │ │ │ 掉线后行为: (•)退出程序 ( )警告提示 ( )忽略 │ │ │ └─────────────────────────────────────────────────────────┘ │ │ │ │ ┌─────────────────────────────────────────────────────────┐ │ │ │ 🛡️ 限流配置 │ │ │ │ │ │ │ │ [✓] 启用请求限流 │ │ │ │ IP每分钟限制: [100] 次 │ │ │ │ 设备每分钟限制: [50] 次 │ │ │ │ 封禁时长: [5] 分钟 │ │ │ └─────────────────────────────────────────────────────────┘ │ │ │ │ ┌─────────────────────────────────────────────────────────┐ │ │ │ ⚠️ 风控配置 │ │ │ │ │ │ │ │ [✓] 启用风控检测 │ │ │ │ [✓] 异地登录检测 │ │ │ │ [✓] 设备变更检测 │ │ │ │ [ ] 自动封禁异常 │ │ │ └─────────────────────────────────────────────────────────┘ │ │ │ │ ┌─────────────────────────────────────────────────────────┐ │ │ │ 📢 客户端显示 │ │ │ │ │ │ │ │ 系统名称: [软件授权管理系统 ] │ │ │ │ Logo地址: [https://... ] │ │ │ │ │ │ │ │ 公告标题: [系统维护通知 ] │ │ │ │ 公告内容: [将于今晚23:00进行维护... ] │ │ │ │ [✓] 显示公告 │ │ │ │ │ │ │ │ 联系方式URL: [https://support.example.com] │ │ │ │ 帮助文档URL: [https://docs.example.com ] │ │ │ │ [ ] 在客户端显示余额 │ │ │ └─────────────────────────────────────────────────────────┘ │ │ │ │ ┌─────────────────────────────────────────────────────────┐ │ │ │ 📊 其他配置 │ │ │ │ │ │ │ │ [ ] 开放用户注册 │ │ │ │ 日志保留天数: [90] 天 │ │ │ └─────────────────────────────────────────────────────────┘ │ │ │ └─────────────────────────────────────────────────────────────────┘ ``` ### 13.2 配置 API ``` # 获取所有配置(分组) GET /api/admin/configs Response: { "code": 200, "data": { "feature": [ { "key": "heartbeat", "value": "true", "displayName": "启用心跳验证" }, { "key": "device_bind", "value": "true", "displayName": "启用设备绑定" }, ... ], "auth": [ { "key": "max_devices", "value": "1", "displayName": "最大设备数" }, ... ], ... } } # 批量更新配置 PUT /api/admin/configs Request: { "feature.heartbeat": "false", "auth.max_devices": "2", "heartbeat.interval": "90" } Response: { "code": 200, "message": "配置已更新" } # 获取客户端可读的公开配置 GET /api/client/config Response: { "code": 200, "data": { "heartbeat": true, "autoUpdate": true, "noticeTitle": "系统维护通知", "noticeContent": "将于今晚23:00进行维护...", "helpUrl": "https://docs.example.com" } } ``` ### 13.3 配置读取服务(后端) ```csharp // 配置服务 - 从数据库读取,无硬编码 public class ConfigService { private readonly IMemoryCache _cache; private readonly AppDbContext _db; public async Task GetAsync(string key, string defaultValue = "") { // 先从缓存读取 var cached = await _cache.GetAsync($"config:{key}"); if (cached != null) return cached; // 从数据库读取 var config = await _db.SystemConfigs .Where(c => c.ConfigKey == key) .FirstOrDefaultAsync(); var value = config?.ConfigValue ?? defaultValue; // 缓存5分钟 await _cache.SetAsync($"config:{key}", value, TimeSpan.FromMinutes(5)); return value; } public async Task IsEnabledAsync(string featureKey) { var value = await GetAsync($"feature.{featureKey}", "false"); return value.Equals("true", StringComparison.OrdinalIgnoreCase); } public async Task GetIntAsync(string key, int defaultValue = 0) { var value = await GetAsync(key, defaultValue.ToString()); return int.TryParse(value, out var result) ? result : defaultValue; } } // 使用示例 public class AuthService { private readonly ConfigService _config; public async Task VerifyAsync(string cardKey) { // 检查功能开关 - 是否需要设备绑定 var needBind = await _config.IsEnabledAsync("device_bind"); if (needBind) { // 执行设备绑定验证... } // 检查功能开关 - 是否启用心跳 var enableHeartbeat = await _config.IsEnabledAsync("heartbeat"); if (enableHeartbeat) { // 返回心跳配置给客户端... } return true; } } ``` ### 13.4 功能开关说明 | 开关 | 关闭后行为 | 使用场景 | |------|-----------|---------| | `feature.heartbeat` | 客户端不需要发送心跳 | 离线环境、调试 | | `feature.device_bind` | 卡密可在任意设备使用 | 临时测试、多设备需求 | | `feature.auto_update` | 客户端不检查更新 | 固定版本部署 | | `feature.agent_system` | 隐藏代理商相关菜单 | 无代理商场景 | | `feature.card_renewal` | 不显示续费功能 | 一次性买断 | | `feature.trial_mode` | 允许未激活卡密试用X天 | 营销推广 | --- ## 十四、SDK 集成开发文档(管理后台内置) ### 14.1 快速开始 #### 步骤 1:获取项目信息 在管理后台创建项目后,会获得以下信息: ```json { "projectId": "PROJ_001", "projectKey": "abcd1234efgh5678", "projectSecret": "xyz999aaa888bbb777", "apiEndpoint": "https://api.example.com" } ``` #### 步骤 2:安装 SDK ```bash # NuGet 包 Install-Package YourCompany.License.Sdk # 或 .NET CLI dotnet add package YourCompany.License.Sdk ``` ### 14.2 基础集成代码 ```csharp using YourCompany.License.Sdk; // 1. 初始化客户端 var licenseClient = new LicenseClient(new LicenseConfig { ProjectId = "PROJ_001", ProjectKey = "abcd1234efgh5678", ProjectSecret = "xyz999aaa888bbb777", ApiEndpoint = "https://api.example.com" }); // 2. 验证卡密(在软件启动时调用) var result = await licenseClient.VerifyAsync(userInputCardKey); if (!result.IsValid) { MessageBox.Show($"验证失败:{result.Message}"); Application.Current.Shutdown(); return; } // 3. 启动心跳(后台服务) await licenseClient.StartHeartbeatAsync(result.AccessToken); // 4. 软件正常运行... ``` ### 14.3 WPF 应用完整示例 ```csharp // App.xaml.cs public partial class App : Application { private LicenseClient _licenseClient; private ILicenseInfo _licenseInfo; protected override async void OnStartup(StartupEventArgs e) { base.OnStartup(e); // 显示登录窗口 var loginWindow = new LoginWindow(); if (loginWindow.ShowDialog() != true) { Shutdown(); return; } var cardKey = loginWindow.CardKey; // 初始化并验证 _licenseClient = new LicenseClient(new LicenseConfig { ProjectId = "PROJ_001", ProjectKey = "abcd1234efgh5678", ProjectSecret = "xyz999aaa888bbb777", ApiEndpoint = "https://api.example.com" }); var result = await _licenseClient.VerifyAsync(cardKey); if (!result.IsValid) { MessageBox.Show($"授权验证失败:{result.Message}", "错误", MessageBoxButton.OK, MessageBoxImage.Error); Shutdown(); return; } _licenseInfo = result; // 启动心跳 _licenseClient.LicenseExpired += (s, e) => { Dispatcher.Invoke(() => { MessageBox.Show("授权已过期,软件将退出", "提示", MessageBoxButton.OK, MessageBoxImage.Warning); Shutdown(); }); }; await _licenseClient.StartHeartbeatAsync(result.AccessToken); // 显示主窗口 var mainWindow = new MainWindow(); mainWindow.Closed += async (s, args) => { await _licenseClient.StopHeartbeatAsync(); }; mainWindow.Show(); } } ``` ### 14.4 控制台应用示例 ```csharp class Program { static async Task Main(string[] args) { Console.Write("请输入卡密:"); var cardKey = Console.ReadLine(); var licenseClient = new LicenseClient(new LicenseConfig { ProjectId = "PROJ_001", ProjectKey = "abcd1234efgh5678", ProjectSecret = "xyz999aaa888bbb777", ApiEndpoint = "https://api.example.com" }); var result = await licenseClient.VerifyAsync(cardKey); if (!result.IsValid) { Console.WriteLine($"验证失败:{result.Message}"); return; } Console.WriteLine($"验证成功!到期时间:{result.ExpireTime}"); Console.WriteLine($"剩余天数:{result.RemainingDays}"); // 使用 CancellationToken 处理授权失效 var cts = new CancellationTokenSource(); licenseClient.LicenseExpired += (s, e) => { Console.WriteLine("授权已失效,程序将退出..."); cts.Cancel(); }; await licenseClient.StartHeartbeatAsync(result.AccessToken); // 主程序逻辑 while (!cts.Token.IsCancellationRequested) { // 你的业务代码 await Task.Delay(1000); } } } ``` ### 14.5 API 接口说明 #### 验证接口 ```http POST /api/auth/verify Content-Type: application/json { "projectId": "PROJ_001", "keyCode": "A3D7-K2P9-M8N1-Q4W6", "deviceId": "硬件指纹", "timestamp": 1735000000, "signature": "HMAC-SHA256签名" } ``` #### 心跳接口 ```http POST /api/auth/heartbeat Authorization: Bearer {access_token} Content-Type: application/json { "accessToken": "jwt_token", "deviceId": "硬件指纹" } ``` ### 14.6 常见问题 **Q:如何获取机器码(DeviceId)?** A:SDK 会自动生成,无需手动获取。如需自定义: ```csharp var deviceId = MachineCode.GetDeviceId(); ``` **Q:心跳失败会怎样?** A:连续 3 次心跳失败(约 3 分钟),会触发 `LicenseExpired` 事件。 **Q:如何处理网络断开?** A:SDK 内置重试机制,短暂网络波动不会影响使用。 **Q:可以离线使用吗?** A:不可以,必须联网验证。这是安全保障的一部分。 --- ## 十五、运维指南 ### 15.1 环境变量说明 | 变量名 | 必填 | 默认值 | 说明 | |--------|------|--------|------| | `DB_PASSWORD` | ✅ | - | PostgreSQL密码 | | `JWT_SECRET` | ✅ | - | JWT签名密钥(≥32字符) | | `ADMIN_PASSWORD` | ❌ | admin123 | 初始管理员密码 | | `ASPNETCORE_ENVIRONMENT` | ❌ | Production | 运行环境 | | `Redis__ConnectionString` | ❌ | redis:6379 | Redis连接字符串 | | `Jwt__ExpireMinutes` | ❌ | 1440 | Token过期时间(分钟) | | `RateLimit__IpPerMinute` | ❌ | 100 | IP每分钟请求限制 | ### 15.2 日志管理 **日志位置:** ``` /opt/license/data/logs/ ├── app-20251230.log # 应用日志(按天滚动) ├── access-20251230.log # 访问日志 └── error-20251230.log # 错误日志 ``` **查看实时日志:** ```bash # Docker日志 docker compose logs -f api # 应用日志 tail -f /opt/license/data/logs/app-$(date +%Y%m%d).log ``` **日志清理(自动):** ```bash # 添加到crontab,保留30天日志 0 4 * * * find /opt/license/data/logs -name "*.log" -mtime +30 -delete ``` ### 15.3 备份与恢复 **手动备份:** ```bash # 备份数据库 docker exec license-db pg_dump -U license license > backup_$(date +%Y%m%d).sql # 备份完整数据目录 tar -czvf license_backup_$(date +%Y%m%d).tar.gz /opt/license/data/ ``` **恢复数据库:** ```bash # 停止API服务 docker compose stop api # 恢复数据库 cat backup_20251230.sql | docker exec -i license-db psql -U license license # 启动API服务 docker compose start api ``` **恢复完整数据:** ```bash docker compose down tar -xzvf license_backup_20251230.tar.gz -C / docker compose up -d ``` ### 15.4 常见问题排查 #### 问题1:API无法访问 ```bash # 检查容器状态 docker compose ps # 查看API日志 docker compose logs api --tail 100 # 检查端口 netstat -tlnp | grep 39256 # 检查健康状态 curl http://127.0.0.1:39256/health/ready ``` #### 问题2:数据库连接失败 ```bash # 检查PostgreSQL状态 docker exec license-db pg_isready -U license # 查看数据库日志 docker compose logs db --tail 50 # 测试连接 docker exec -it license-db psql -U license -c "SELECT 1;" ``` #### 问题3:Redis连接失败 ```bash # 检查Redis状态 docker exec license-redis redis-cli ping # 查看内存使用 docker exec license-redis redis-cli info memory ``` #### 问题4:卡密验证失败 ```bash # 查看验证日志 docker compose logs api 2>&1 | grep -i "verify" # 检查卡密状态 docker exec license-db psql -U license -c "SELECT * FROM CardKeys WHERE KeyCode = 'XXXX-XXXX-XXXX-XXXX';" # 检查设备绑定 docker exec license-db psql -U license -c "SELECT * FROM Devices WHERE CardKeyId = 123;" ``` ### 15.5 性能监控 **基础监控脚本:** ```bash #!/bin/bash # /opt/license/scripts/monitor.sh echo "=== System Status ===" echo "CPU: $(top -bn1 | grep 'Cpu(s)' | awk '{print $2}')%" echo "Memory: $(free -m | awk 'NR==2{printf "%s/%sMB (%.2f%%)", $3,$2,$3*100/$2 }')" echo "Disk: $(df -h / | awk 'NR==2{print $5}')" echo "" echo "=== Docker Status ===" docker compose ps echo "" echo "=== API Health ===" curl -s http://127.0.0.1:39256/health/ready | jq . echo "" echo "=== Recent Errors ===" tail -5 /opt/license/data/logs/error-$(date +%Y%m%d).log 2>/dev/null || echo "No errors today" ``` **添加监控告警(简单版):** ```bash #!/bin/bash # /opt/license/scripts/alert.sh # 检查API健康状态 if ! curl -sf http://127.0.0.1:39256/health/live > /dev/null; then # 发送告警(可替换为钉钉/邮件通知) echo "[ALERT] License API is down at $(date)" >> /var/log/license-alert.log # 尝试重启 docker compose restart api fi ``` ```bash # 每分钟检查一次 * * * * * /opt/license/scripts/alert.sh ``` ### 15.6 版本升级流程 ```bash # 1. 备份当前数据 /opt/license/scripts/backup.sh # 2. 拉取新版本代码 cd /opt/license git pull origin main # 3. 重新构建镜像 docker compose build api # 4. 滚动更新(无停机) docker compose up -d --no-deps api # 5. 检查新版本状态 docker compose logs -f api # 6. 如有问题,回滚 docker compose down docker compose up -d --build api # 使用上一个镜像 ``` ### 15.7 安全检查清单 **定期检查(每周):** - [ ] 检查磁盘空间 `df -h` - [ ] 检查备份是否正常执行 - [ ] 检查异常登录日志 - [ ] 更新系统安全补丁 **定期检查(每月):** - [ ] 检查SSL证书有效期 `certbot certificates` - [ ] 检查数据库表膨胀 `VACUUM ANALYZE` - [ ] 清理过期卡密和日志 - [ ] 审计管理员操作记录