Files
kami/软件授权系统-综合方案.md
2026-01-04 23:00:21 +08:00

144 KiB
Raw Blame History

软件授权系统 - 综合设计方案

一、需求分析

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项目表

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卡密表

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设备表

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访问日志表

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统计表

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管理员表

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代理商表

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代理商额度流水

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卡密操作日志

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系统配置表 - 配置驱动核心)

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 卡密激活(乐观锁)

-- 使用乐观锁防止并发激活
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

-- 设备绑定使用 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 心跳更新(批量优化)

-- 批量更新心跳时间(减少数据库压力)
UPDATE Devices
SET LastHeartbeat = NOW()
WHERE Id = ANY(:deviceIds) AND DeletedAt IS NULL;

3.11.5 软删除查询模式

-- 所有查询默认过滤已删除数据
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 幂等性支持表

-- 幂等性键存储(防止重复请求)
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 统一响应格式

{
  "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: <UUID>  # 唯一请求标识24小时内有效

示例:
POST /api/admin/cards/generate
Headers:
  Authorization: Bearer <token>
  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": <Excel/CSV文件>,
    "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) 机器码生成(硬件指纹)

public static class MachineCode
{
    public static string GetDeviceId()
    {
        var factors = new List<string>
        {
            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) 请求签名

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) 防篡改检测

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) 心跳机制

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<HeartbeatResponse> 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<HeartbeatResponse>(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登录 自动封禁,需申诉

风控规则实现示例:

public class RiskControl
{
    private readonly IRedisCache _cache;

    public async Task<RiskResult> CheckAsync(VerifyRequest request)
    {
        var rules = new List<Func<Task<RiskResult>>>
        {
            () => 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<RiskResult> 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 服务端加密实现示例

public class SoftwareEncryptionService
{
    private readonly IStorageService _storage;
    private readonly RSA _clientRsa; // 客户端公钥

    public async Task<SoftwareVersion> 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 客户端解密实现示例

public class SoftwareDownloader
{
    private readonly RSA _privateKey; // 内置的客户端私钥

    public async Task<string> 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 生成逻辑

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<string>();
        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 完整配置

# 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 环境变量配置

# .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 配置

# /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 数据库备份脚本

#!/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"

添加定时任务:

# crontab -e
0 3 * * * /opt/license/scripts/backup.sh >> /var/log/license-backup.log 2>&1

8.7 快速部署步骤

# 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
  • 卡密管理 APICRUD + 批量生成)
  • 设备绑定 + 心跳验证
  • 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 张卡密                                              │
│                                                                  │
│  批次IDbatch_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 配置读取服务(后端)

// 配置服务 - 从数据库读取,无硬编码
public class ConfigService
{
    private readonly IMemoryCache _cache;
    private readonly AppDbContext _db;

    public async Task<string> GetAsync(string key, string defaultValue = "")
    {
        // 先从缓存读取
        var cached = await _cache.GetAsync<string>($"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<bool> IsEnabledAsync(string featureKey)
    {
        var value = await GetAsync($"feature.{featureKey}", "false");
        return value.Equals("true", StringComparison.OrdinalIgnoreCase);
    }

    public async Task<int> 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<bool> 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获取项目信息

在管理后台创建项目后,会获得以下信息:

{
  "projectId": "PROJ_001",
  "projectKey": "abcd1234efgh5678",
  "projectSecret": "xyz999aaa888bbb777",
  "apiEndpoint": "https://api.example.com"
}

步骤 2安装 SDK

# NuGet 包
Install-Package YourCompany.License.Sdk

# 或 .NET CLI
dotnet add package YourCompany.License.Sdk

14.2 基础集成代码

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 应用完整示例

// 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 控制台应用示例

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 接口说明

验证接口

POST /api/auth/verify
Content-Type: application/json

{
  "projectId": "PROJ_001",
  "keyCode": "A3D7-K2P9-M8N1-Q4W6",
  "deviceId": "硬件指纹",
  "timestamp": 1735000000,
  "signature": "HMAC-SHA256签名"
}

心跳接口

POST /api/auth/heartbeat
Authorization: Bearer {access_token}
Content-Type: application/json

{
  "accessToken": "jwt_token",
  "deviceId": "硬件指纹"
}

14.6 常见问题

Q如何获取机器码DeviceId ASDK 会自动生成,无需手动获取。如需自定义:

var deviceId = MachineCode.GetDeviceId();

Q心跳失败会怎样 A连续 3 次心跳失败(约 3 分钟),会触发 LicenseExpired 事件。

Q如何处理网络断开 ASDK 内置重试机制,短暂网络波动不会影响使用。

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    # 错误日志

查看实时日志:

# Docker日志
docker compose logs -f api

# 应用日志
tail -f /opt/license/data/logs/app-$(date +%Y%m%d).log

日志清理(自动):

# 添加到crontab保留30天日志
0 4 * * * find /opt/license/data/logs -name "*.log" -mtime +30 -delete

15.3 备份与恢复

手动备份:

# 备份数据库
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/

恢复数据库:

# 停止API服务
docker compose stop api

# 恢复数据库
cat backup_20251230.sql | docker exec -i license-db psql -U license license

# 启动API服务
docker compose start api

恢复完整数据:

docker compose down
tar -xzvf license_backup_20251230.tar.gz -C /
docker compose up -d

15.4 常见问题排查

问题1API无法访问

# 检查容器状态
docker compose ps

# 查看API日志
docker compose logs api --tail 100

# 检查端口
netstat -tlnp | grep 39256

# 检查健康状态
curl http://127.0.0.1:39256/health/ready

问题2数据库连接失败

# 检查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;"

问题3Redis连接失败

# 检查Redis状态
docker exec license-redis redis-cli ping

# 查看内存使用
docker exec license-redis redis-cli info memory

问题4卡密验证失败

# 查看验证日志
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 性能监控

基础监控脚本:

#!/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"

添加监控告警(简单版):

#!/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
# 每分钟检查一次
* * * * * /opt/license/scripts/alert.sh

15.6 版本升级流程

# 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
  • 清理过期卡密和日志
  • 审计管理员操作记录