
我打算开发一款应用激活系统
目前整个 LicenseServer 商业授权系统 的规划进度如下:
| 章节 | 内容 | 状态 |
|---|---|---|
| 第一部分 | 整体架构设计与技术选型 | ✅ 完成 |
| 第二部分 | 安全模型与授权体系设计 | ✅ 完成 |
| 第三部分 | 部署架构设计(Docker/Kubernetes/云部署) | ✅ 完成 |
| 第四部分 | 数据库详细设计 | ✅ 完成 |
| 第五部分 | 安全增强设计 | ✅ 完成 |
| 第六部分 | 项目工程规范、CI/CD、开发流程 | ✅ 完成 |
| 第七部分 | 高级安全体系(密钥、签名、防破解等) | ✅ 完成 |
| 第八部分(一) | 数据库与后端核心代码 | ✅ 完成 |
| 第八部分(二) | Identity + JWT + Refresh Token认证系统 | ✅ 完成 |
| 第八部分(三) | License核心业务实现 | ✅ 完成 |
| 第八部分(四) | 客户端SDK实现 | ✅ 完成 |
| 第八部分(五) | 生产级安全加固 | ✅ 完成 |
| 第八部分(六) | 支付系统 + 自动发货 + 商业闭环 | ✅ 完成 |
| 第八部分(七) | Admin Console管理后台 | ⬅️ 当前开始 |
| 第八部分(八) | 完整部署上线方案 | 待完成 |
| 第八部分(九) | 客户端商业化加固与发布流程 | 待完成 |
| 第八部分(十) | 最终源码目录、Docker部署文件、生产配置 | 待完成 |
你描述的系统本质上是一个商业软件 License(许可证)管理平台,类似于:
- JetBrains License Server
- Microsoft Product Activation
- Adobe Licensing
- Paddle / Lemon Squeezy + 自建 License Server
- Cryptlex、Keygen.sh 等商业授权系统
它不是简单的“生成激活码”,而是一个包含:
- 订单绑定
- 用户身份管理
- License 生成
- 设备指纹绑定
- 激活验证
- 离线授权(可选)
- 防破解、防爆破
- 后台权限控制
- 审计日志
的完整系统。
根据你的技术背景(你熟悉 C#、Node.js、PostgreSQL、Docker、CI/CD,也考虑 Web3/AI 全栈方向),我建议不要用传统 PHP,而采用现代云原生架构。
推荐技术栈(商业级方案)
我个人推荐:
后端:Go / C# ASP.NET Core
数据库:PostgreSQL
管理后台:Next.js + TypeScript
客户端 SDK:C++ / Rust / C#
部署:Docker + Kubernetes(后期)
方案一(我最推荐):Go + PostgreSQL + Next.js
架构:
用户
|
|
License Website
Next.js + Tailwind
|
|
API Gateway
|
Go API
|
----------------
| |
PostgreSQL Redis
|
License Database
后端
Go
推荐:
- Gin
- Fiber
- Echo
原因:
1. 非常适合 License Server
授权服务器特点:
- 请求量不一定巨大
- 但是安全要求高
- 高并发验证
- 低延迟
Go非常适合。
例如:
用户打开软件:
软件
|
| license_key
|
v
https://api.xxx.com/activate
返回:
{
status:"success",
expire:"2027-08-01",
device_limit:3,
signature:"xxxx"
}
Go处理非常舒服。
2. 编译成单文件
例如:
license-server
部署:
docker run license-server
简单。
方案二:C# ASP.NET Core(其实也非常适合你)
你的背景有 .NET,所以:
ASP.NET Core 9
+
Entity Framework Core
+
PostgreSQL
+
Identity
也是优秀方案。
优点:
- 企业级安全成熟
- JWT
- OAuth
- RBAC权限
- 加密库成熟
- Windows生态强
如果你的软件客户端也是:
- Windows
- .NET
- WPF
- WinUI
那么我甚至更推荐 C#。
架构:
ASP.NET Core API
|
|
PostgreSQL
|
Identity Server
|
Admin Panel
前端管理后台
推荐:
Next.js
原因:
你的激活系统一定需要:
- 用户中心
- 登录
- 找回 License
- 订单查询
- 设备管理
- 管理员后台
技术:
Next.js 16
+
TypeScript
+
Tailwind CSS
+
shadcn/ui
页面:
用户:
/login
/register
/licenses
/devices
管理员:
/admin/users
/admin/licenses
/admin/logs
数据库设计
核心表:
users
用户:
id
email
password_hash
created_at
licenses
激活码:
id
license_key_hash
product_id
max_devices
expire_date
status
created_at
注意:
不要保存明文:
不要:
48D0-61CD-35FE...
保存:
SHA256(
48D0-61CD...
)
数据库里面:
a91fc92b88a7...
即使数据库泄露:
攻击者拿不到激活码。
devices
设备绑定:
id
license_id
device_hash
device_name
os
first_activate
last_check
例如:
电脑生成:
CPU ID
+
Motherboard UUID
+
Disk serial
然后:
SHA256:
A82B9F883...
服务器只保存:
设备指纹Hash
激活流程设计
第一次激活
用户输入:
License:
48D0-61CD-35FE-9C9B
Email:
test@gmail.com
客户端生成:
device_id
发送:
POST /activate
{
license:
"xxxx",
device:
"a82f92..."
}
服务器:
检查:
License存在?
↓
是否超过设备数量?
↓
绑定设备
↓
生成授权Token
返回:
{
license_status:"active",
token:
"eyxxxx",
signature:
"xxxx"
}
防破解核心:不要返回明文授权信息
错误方式:
返回:
{
"user":"Tom",
"expire":"2027",
"max_device":3
}
别人抓包修改即可。
正确:
使用:
非对称加密
服务器:
私钥:
private.key
客户端:
公钥:
public.key
服务器:
签名:
license data
+
RSA/ECDSA signature
客户端:
验证:
public key
↓
signature verify
即使破解客户端:
没有服务器私钥:
无法生成新License。
推荐:
ECDSA P-256
或者
Ed25519
不要用 RSA 2048 新项目。
防爆破设计
1. License不要顺序生成
不要:
000001
000002
000003
应该:
随机:
48D0-61CD-35FE-9C9B-5358-3725-D644-1A18
128bit以上随机。
2. 激活接口限流
例如:
Redis:
IP:
一分钟最多10次
License:
一分钟最多5次
使用:
- Redis Rate Limit
- Cloudflare WAF
3. 登录保护
必须:
- Argon2id密码Hash
- MFA
- 登录失败锁定
密码:
不要:
bcrypt
推荐:
Argon2id
防止内部人员查看License
你提出的这个非常重要。
比如:
管理员:
SELECT * FROM licenses
看到:
48D0-61CD...
这是风险。
方案:
数据库加密存储
保存:
license_hash
+
encrypted_license
例如:
license:
AES-256-GCM
加密
密钥:
不要放数据库。
使用:
- Hashicorp Vault
- AWS KMS
- Azure Key Vault
结构:
Database
encrypted_license
|
|
Vault
master key
权限设计
RBAC:
角色:
super_admin
license_admin
support
readonly
客服:
只能:
查询邮箱
重置设备
不能:
查看License原文
客户端保护
如果你的软件:
Windows
推荐:
C++
或者:
C# + Native DLL
不要:
纯 .NET EXE
因为容易:
- ILSpy
- dnSpy
方案:
Main.exe
+
License.dll(C++)
关键验证:
放Native。
推荐最终架构
如果我是设计商业产品:
Cloudflare
|
Nginx
|
Next.js Website
|
ASP.NET Core / Go
|
--------------------------------
PostgreSQL Redis
|
Vault/KMS
|
License SDK
Windows/Mac/Linux
语言选择排序(针对你的情况)
| 排名 | 技术 | 推荐 |
|---|---|---|
| 1 | C# ASP.NET Core | ★★★★★ |
| 2 | Go | ★★★★★ |
| 3 | Rust | ★★★★ |
| 4 | Node.js NestJS | ★★★★ |
| 5 | Java Spring Boot | ★★★ |
| 6 | PHP Laravel | ★★ |
结合你的背景:
- 已经会 .NET
- 做过全栈
- 熟悉 Docker
- 熟悉 PostgreSQL
我建议:
第一版:
ASP.NET Core 9
+
PostgreSQL
+
Redis
+
Next.js
+
Docker
第二版:
加入:
Vault
+
KMS
+
客户端SDK
+
离线License
这个技术路线可以做到接近商业授权系统的安全等级。你后续如果开发的是 Windows 桌面软件(尤其 WinUI/WPF/.NET),C# 生态会更加顺手。
非常好,这一步其实比前面的设计更重要。
前面的文档解决的是系统怎么设计,但是一个商业项目真正开始开发时,最大的问题通常是:
- Mac、Windows、Linux环境不一致
- Node/npm版本混乱
- 数据库版本不同导致迁移失败
- Docker环境差异
- 前端依赖锁定问题
- CI/CD和本地开发不一致
所以我建议单独建立一份:
LicenseServer 商业授权系统
多平台开发与生产部署规范文档 v1.0
目标:
在 macOS、Windows、Linux 三个平台拥有完全一致的开发体验。
第一章:总体技术路线选择
最终推荐架构:
Frontend
|
|
Next.js + TypeScript
|
|
ASP.NET Core 10 Web API
|
|
PostgreSQL
|
|
Redis
|
|
Docker
客户端:
Windows
|
C# / Avalonia / WPF
macOS
|
.NET + Native
Linux
|
.NET
第二章:开发环境统一原则
核心原则
不要依赖:
- 系统Node
- 系统Python
- 系统数据库
- 系统.NET
全部版本固定。
采用:
mise
+
Docker
+
Dev Container
第三章:开发工具版本建议
1. Git
所有平台:
Git >= 2.45
检查:
git --version
2. VS Code
推荐:
VS Code Stable
插件统一:
C# Dev Kit
ESLint
Prettier
Docker
PostgreSQL
GitLens
Tailwind CSS IntelliSense
3. mise版本管理
我建议你继续使用 mise。
原因:
你之前已经使用过:
- Go
- Flutter
- Python
mise 非常适合多语言项目。
安装:
mac/Linux:
curl https://mise.run | sh
Windows:
PowerShell:
winget install jdx.mise
第四章:统一语言版本
一、C# / .NET
推荐:
.NET 10 LTS
原因:
你的项目:
- 商业授权服务器
- 长期维护
- 企业软件
不要追最新preview。
安装:
检查:
dotnet --version
项目:
global.json
{
"sdk":
{
"version":
"10.0.100",
"rollForward":
"latestPatch"
}
}
作用:
所有机器:
Mac:
10.0.100
Windows:
10.0.100
Linux:
10.0.100
二、Node.js
这里重点回答你的问题:
Node.js 能不能换 Bun?
答案:
开发环境可以,商业项目核心构建不建议完全替代。
原因:
Bun现在非常优秀:
优势:
- 安装快
- JS运行速度快
- 自带bundler
- 自带test
但是:
大型商业项目:
尤其:
- Next.js
- Prisma
- Tailwind
- shadcn
- 企业CI
Node兼容性仍然最好。
我的建议:
推荐方案
开发:
Bun
可以。
生产:
Node.js LTS
CI:
Node.js LTS
也就是说:
开发机器
Mac:
bun
Windows:
bun
Linux:
bun
CI/CD:
node
Production:
node
三、Bun版本
固定:
bun 1.2+
使用:
bun --version
项目:
.bun-version
1.2.20
第五章:TypeScript版本建议
你问:
TypeScript 7.0怎么样?
TypeScript 7.0 已经进入新的原生编译器路线,官方目标是提升编译速度和降低内存使用,采用新的原生实现。
但是商业项目需要考虑生态兼容。
我的建议:
2026新项目:
可以使用:
TypeScript 7.x
但是:
需要锁版本。
例如:
package.json:
{
"devDependencies":
{
"typescript":
"7.0.2"
}
}
不要:
^7.0.0
避免自动升级。
第六章:Next.js版本
推荐:
Next.js 16
原因:
你的项目:
Admin Console
官网
用户中心
推荐:
Next.js
+
React
+
TypeScript
+
Tailwind
+
shadcn/ui
package:
{
"dependencies":
{
"next":
"16.x",
"react":
"19.x"
}
}
第七章:数据库版本选择
这是商业系统最关键。
PostgreSQL
推荐:
PostgreSQL 17
原因:
成熟:
- JSON性能好
- 索引优秀
- 长期支持
不要使用:
latest
Docker:
postgres:17
不要:
postgres:latest
第八章:ORM选择
.NET:
推荐:
Entity Framework Core
版本:
EF Core 10
原因:
官方生态。
数据库迁移:
统一:
dotnet ef migrations add Initial
第九章:Redis版本
推荐:
Redis 7.4 LTS
用途:
- Session
- Cache
- Rate limit
- License查询缓存
Docker:
redis:7.4
第十章:消息队列
第一版:
不要复杂化。
不用:
Kafka
推荐:
Redis Queue
或者:
RabbitMQ
版本:
RabbitMQ 4.x
以后:
百万用户:
Kafka。
第十一章:搜索系统
第一版:
PostgreSQL全文搜索。
不要马上:
ElasticSearch。
原因:
增加:
- 内存
- 运维
- 安全
第十二章:完整开发环境
最终:
Developer Machine
macOS
Windows
Linux
|
|
mise
|
|
Docker
|
|
PostgreSQL
Redis
Mailhog
|
|
ASP.NET Core
|
|
Next.js
第十三章:项目根目录配置
最终:
LicenseServer/
├── .mise.toml
├── global.json
├── docker-compose.dev.yml
├── package.json
├── bun.lock
├── Directory.Build.props
├── backend/
├── frontend/
├── client/
└── docs/
第十四章:mise配置
.mise.toml
[tools]
dotnet="10.0"
node="24"
bun="1.2"
python="3.13"
go="1.25"
所有开发者:
执行:
mise install
自动安装。
第十五章:Docker开发环境
docker-compose.dev.yml
services:
postgres:
image:
postgres:17
environment:
POSTGRES_PASSWORD:
password
ports:
- 5432:5432
redis:
image:
redis:7.4
ports:
- 6379:6379
启动:
docker compose -f docker-compose.dev.yml up -d
第十六章:邮件测试
不要开发阶段发真实邮件。
使用:
Mailhog
Docker:
mailhog:
image:
mailhog/mailhog
ports:
- 8025:8025
浏览:
localhost:8025
第十七章:跨平台注意事项
路径
不要:
C:\project
代码:
使用:
/
换行符
Git:
配置:
git config --global core.autocrlf false
统一:
LF。
文件权限
Linux:
注意:
chmod
Docker:
不要root运行。
第十八章:IDE统一
推荐:
全部:
VS Code。
原因:
Mac:
✅
Windows:
✅
Linux:
✅
第十九章:生产服务器版本
推荐:
Ubuntu Server 24.04 LTS
Docker 29.x
PostgreSQL 17
Redis 7.4
Nginx 1.26+
.NET 10
第二十章:最终推荐技术栈
Backend
| 组件 | 版本 |
|---|---|
| .NET | 10 LTS |
| ASP.NET Core | 10 |
| EF Core | 10 |
| PostgreSQL | 17 |
| Redis | 7.4 |
| Serilog | 最新版稳定版 |
Frontend
| 组件 | 版本 |
|---|---|
| Next.js | 16 |
| React | 19 |
| TypeScript | 7.0.x |
| Tailwind | 4 |
| shadcn/ui | latest |
Runtime
| 环境 | 版本 |
|---|---|
| Node | 24 LTS |
| Bun | 1.2 |
| Docker | 29 |
| Git | 2.45+ |
第二十一章:我的最终建议
针对你的情况(Mac + PC + Linux,多系统开发,并且目标是商业软件):
我建议:
Backend:
C# .NET 10 LTS
Frontend:
Next.js 16
TypeScript 7
Package:
Bun开发
Node生产
Database:
PostgreSQL 17
Cache:
Redis 7.4
Environment:
mise
Deployment:
Docker Compose
CI:
GitHub Actions
这是目前比较稳,同时又保持现代化的方案。
下一部分我建议继续写:
第九部分:从零开始创建 LicenseServer 实际开发教程
内容:
- 创建 Git 仓库
- 初始化 .NET Solution
- 创建 Clean Architecture
- 配置 PostgreSQL + EF Core
- 创建 User / License Entity
- 第一个 API
- Docker 开发环境启动
- Mac/Windows/Linux 三端验证流程
这一部分会开始进入真正编码阶段。
好的
LicenseServer 商业授权系统开发实战文档 v1.0
第九部分:从零开始创建 LicenseServer 实际开发教程
第一阶段:项目初始化与基础架构搭建
目标:
完成:
✅ Git仓库 ✅ 多平台开发环境 ✅ .NET Solution ✅ Clean Architecture ✅ PostgreSQL ✅ EF Core ✅ Docker开发环境 ✅ 第一个API接口
开发环境支持:
- macOS(Intel / Apple Silicon)
- Windows 10/11
- Linux(Ubuntu/Debian/Arch)
1. 创建项目目录
统一目录:
例如:
mac/Linux:
mkdir LicenseServer
cd LicenseServer
Windows PowerShell:
mkdir LicenseServer
cd LicenseServer
最终:
LicenseServer/
作为项目根目录。
2. 初始化Git仓库
执行:
git init
创建:
.gitignore
.NET模板:
dotnet new gitignore
追加:
# Node
node_modules/
.next/
dist/
# Environment
.env
.env.*
# Docker
postgres-data/
# IDE
.vscode/
# OS
.DS_Store
Thumbs.db
3. 初始化项目文档
创建:
docs/
结构:
docs/
├── architecture.md
├── database.md
├── api.md
├── deployment.md
└── security.md
4. 创建.NET Solution
进入根目录:
dotnet new sln \
-n LicenseServer
生成:
LicenseServer.sln
5. 创建后端目录
创建:
mkdir backend
进入:
cd backend
6. 创建Clean Architecture
最终结构:
backend/
├── LicenseServer.API
├── LicenseServer.Application
├── LicenseServer.Domain
├── LicenseServer.Infrastructure
└── LicenseServer.Tests
7. 创建Domain项目
dotnet new classlib \
-n LicenseServer.Domain
移动:
mv LicenseServer.Domain ../
8. 创建Application项目
dotnet new classlib \
-n LicenseServer.Application
9. 创建Infrastructure项目
dotnet new classlib \
-n LicenseServer.Infrastructure
10. 创建API项目
dotnet new webapi \
-n LicenseServer.API
11. 创建测试项目
dotnet new xunit \
-n LicenseServer.Tests
12. 添加项目到Solution
返回根目录:
cd ..
执行:
dotnet sln add \
backend/*/*.csproj
检查:
dotnet sln list
应该看到:
LicenseServer.API
LicenseServer.Application
LicenseServer.Domain
LicenseServer.Infrastructure
LicenseServer.Tests
13. 建立项目引用关系
架构:
API
↓
Application
↓
Domain
Infrastructure
↓
Application
执行:
Application引用Domain
dotnet add \
backend/LicenseServer.Application \
reference \
backend/LicenseServer.Domain
Infrastructure引用Domain
dotnet add \
backend/LicenseServer.Infrastructure \
reference \
backend/LicenseServer.Domain
Infrastructure引用Application
dotnet add \
backend/LicenseServer.Infrastructure \
reference \
backend/LicenseServer.Application
API引用Application
dotnet add \
backend/LicenseServer.API \
reference \
backend/LicenseServer.Application
API引用Infrastructure
dotnet add \
backend/LicenseServer.API \
reference \
backend/LicenseServer.Infrastructure
14. 安装Entity Framework Core
进入:
cd backend/LicenseServer.Infrastructure
安装:
dotnet add package \
Microsoft.EntityFrameworkCore
PostgreSQL驱动:
dotnet add package \
Npgsql.EntityFrameworkCore.PostgreSQL
迁移工具:
dotnet add package \
Microsoft.EntityFrameworkCore.Design
15. 安装配置管理
API:
cd ../LicenseServer.API
安装:
dotnet add package \
Microsoft.Extensions.Configuration.Json
16. 创建Domain实体
进入:
LicenseServer.Domain
创建:
Entities/
结构:
Entities
├── User.cs
├── License.cs
├── Device.cs
└── Product.cs
17. User实体
User.cs
namespace LicenseServer.Domain.Entities;
public class User
{
public Guid Id {get;set;}
public string Email {get;set;} = "";
public string PasswordHash {get;set;}="";
public DateTime CreatedAt {get;set;}
= DateTime.UtcNow;
}
18. License实体
License.cs
namespace LicenseServer.Domain.Entities;
public class License
{
public Guid Id {get;set;}
public Guid UserId {get;set;}
public string KeyHash {get;set;}="";
public int MaxDevices {get;set;}
public DateTime ExpireAt {get;set;}
public bool Active {get;set;}
public User User {get;set;}=null!;
}
19. 创建数据库Context
Infrastructure:
创建:
Database/
LicenseDbContext.cs
代码:
using Microsoft.EntityFrameworkCore;
using LicenseServer.Domain.Entities;
namespace LicenseServer.Infrastructure.Database;
public class LicenseDbContext
: DbContext
{
public LicenseDbContext(
DbContextOptions options)
:base(options)
{
}
public DbSet<User> Users => Set<User>();
public DbSet<License> Licenses => Set<License>();
}
20. 配置API连接数据库
Program.cs
添加:
builder.Services
.AddDbContext<LicenseDbContext>(options =>
{
options.UseNpgsql(
builder.Configuration
.GetConnectionString("Default"));
});
21. appsettings.json
API:
添加:
{
"ConnectionStrings":
{
"Default":
"Host=localhost;Database=license;Username=license;Password=password"
}
}
22. 创建Docker开发数据库
根目录:
创建:
docker-compose.dev.yml
内容:
services:
postgres:
image:
postgres:17
container_name:
license-postgres-dev
environment:
POSTGRES_DB:
license
POSTGRES_USER:
license
POSTGRES_PASSWORD:
password
ports:
- "5432:5432"
volumes:
- postgres-data:/var/lib/postgresql/data
redis:
image:
redis:7.4
container_name:
license-redis-dev
ports:
- "6379:6379"
volumes:
postgres-data:
23. 启动开发数据库
根目录:
docker compose \
-f docker-compose.dev.yml \
up -d
检查:
docker ps
应该看到:
license-postgres-dev
license-redis-dev
24. 创建第一次Migration
进入API:
cd backend/LicenseServer.API
执行:
dotnet ef migrations add InitialCreate \
-p ../LicenseServer.Infrastructure \
-s .
25. 更新数据库
dotnet ef database update \
-p ../LicenseServer.Infrastructure \
-s .
成功:
数据库:
license
├── Users
├── Licenses
└── __EFMigrationsHistory
26. 创建第一个测试API
API:
创建:
Controllers/TestController.cs
代码:
using Microsoft.AspNetCore.Mvc;
namespace LicenseServer.API.Controllers;
[ApiController]
[Route("api/test")]
public class TestController
:ControllerBase
{
[HttpGet]
public IActionResult Get()
{
return Ok(new
{
message="License Server Running",
time=DateTime.UtcNow
});
}
}
27. 启动API
dotnet run
访问:
https://localhost:7000/api/test
返回:
{
"message":
"License Server Running",
"time":
"2026-xx-xx"
}
28. 三平台验证
macOS
dotnet --version
docker --version
git --version
Windows
PowerShell:
dotnet --version
docker version
git --version
Linux
dotnet --version
docker version
git --version
结果必须一致。
29. 当前阶段完成
现在已经拥有:
| 模块 | 状态 |
|---|---|
| Git仓库 | ✅ |
| Solution结构 | ✅ |
| Clean Architecture | ✅ |
| Domain模型 | ✅ |
| EF Core | ✅ |
| PostgreSQL | ✅ |
| Docker开发环境 | ✅ |
| 第一个API | ✅ |
下一阶段:
第九部分(二):实现用户系统 Identity + JWT + Refresh Token
将开始编写真正商业系统核心:
包括:
- ASP.NET Core Identity设计
- 用户注册
- 邮箱验证
- BCrypt密码哈希
- JWT Access Token
- Refresh Token
- Token轮换机制
- 登录设备管理
- MFA设计
- 权限RBAC基础
这一部分完成后,你的 LicenseServer 将拥有真正可用的用户认证系统。
LicenseServer 商业授权系统开发实战文档 v1.0
第九部分(二):用户系统 Identity + JWT + Refresh Token 实现
本阶段开始实现真正商业系统基础能力:
目标:
完成:
✅ 用户注册 ✅ 邮箱账户体系 ✅ 密码安全存储 ✅ 登录认证 ✅ JWT Access Token ✅ Refresh Token ✅ Token轮换 ✅ 登录设备管理 ✅ RBAC权限基础 ✅ 后续MFA扩展接口
一、认证系统整体架构
最终流程:
用户
|
注册
|
Email验证
|
Password Hash
|
PostgreSQL
|
登录
|
验证密码
|
生成:
Access Token
+
Refresh Token
|
客户端保存
|
访问API
|
JWT Middleware验证
二、认证技术选型
采用:
| 功能 | 技术 |
|---|---|
| 用户管理 | ASP.NET Core Identity |
| 密码哈希 | PBKDF2 |
| Token | JWT |
| Refresh Token | 数据库存储 |
| 权限 | RBAC |
| 邮箱验证 | SMTP |
| MFA | TOTP |
三、安装Identity依赖
进入:
cd backend/LicenseServer.Infrastructure
安装:
dotnet add package Microsoft.AspNetCore.Identity.EntityFrameworkCore
JWT:
API项目:
cd ../LicenseServer.API
安装:
dotnet add package Microsoft.AspNetCore.Authentication.JwtBearer
四、修改User实体设计
之前:
User
{
Id
Email
PasswordHash
}
商业版本:
增加:
User
Id
Email
PasswordHash
EmailConfirmed
Status
CreatedAt
LastLoginAt
五、创建ApplicationUser
Domain:
创建:
Entities/ApplicationUser.cs
代码:
using Microsoft.AspNetCore.Identity;
namespace LicenseServer.Domain.Entities;
public class ApplicationUser
: IdentityUser<Guid>
{
public DateTime CreatedAt
{get;set;}
= DateTime.UtcNow;
public DateTime?
LastLoginAt
{get;set;}
public bool IsActive
{get;set;}
=true;
}
六、修改DbContext
替换:
LicenseDbContext
代码:
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Identity.EntityFrameworkCore;
public class LicenseDbContext
:
IdentityDbContext<
ApplicationUser,
IdentityRole<Guid>,
Guid>
{
public LicenseDbContext(
DbContextOptions options)
:base(options)
{
}
public DbSet<License>
Licenses
=>Set<License>();
}
七、Identity数据库结构
迁移后:
自动生成:
AspNetUsers
AspNetRoles
AspNetUserRoles
AspNetUserClaims
AspNetUserTokens
Licenses
Devices
八、配置Identity
Program.cs:
添加:
builder.Services
.AddIdentity<
ApplicationUser,
IdentityRole<Guid>
>(options =>
{
options.Password.RequiredLength=12;
options.Password.RequireDigit=true;
options.Password.RequireUppercase=true;
options.Password.RequireLowercase=true;
options.Password.RequireNonAlphanumeric=true;
options.User.RequireUniqueEmail=true;
})
.AddEntityFrameworkStores<LicenseDbContext>();
九、密码安全策略
商业软件建议:
最低:
12字符
大小写
数字
特殊字符
禁止:
123456
password
qwerty
十、JWT配置
appsettings.json:
{
"Jwt":
{
"Key":
"CHANGE_THIS_SECRET_KEY",
"Issuer":
"LicenseServer",
"Audience":
"LicenseClient",
"ExpireMinutes":
15
}
}
十一、JWT Service
Application:
创建:
Interfaces/IJwtService.cs
代码:
public interface IJwtService
{
string GenerateAccessToken(
ApplicationUser user);
string GenerateRefreshToken();
}
十二、JWT实现
Infrastructure:
创建:
Security/JwtService.cs
代码:
using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims;
using System.Security.Cryptography;
using Microsoft.IdentityModel.Tokens;
public class JwtService
:IJwtService
{
private readonly IConfiguration _config;
public JwtService(
IConfiguration config)
{
_config=config;
}
public string GenerateAccessToken(
ApplicationUser user)
{
var claims=new[]
{
new Claim(
JwtRegisteredClaimNames.Sub,
user.Id.ToString()),
new Claim(
JwtRegisteredClaimNames.Email,
user.Email!)
};
var key=new SymmetricSecurityKey(
Encoding.UTF8.GetBytes(
_config["Jwt:Key"]!)
);
var token=new JwtSecurityToken(
issuer:_config["Jwt:Issuer"],
audience:_config["Jwt:Audience"],
claims:claims,
expires:
DateTime.UtcNow.AddMinutes(15),
signingCredentials:
new SigningCredentials(
key,
SecurityAlgorithms.HmacSha256)
);
return new JwtSecurityTokenHandler()
.WriteToken(token);
}
public string GenerateRefreshToken()
{
return Convert.ToBase64String(
RandomNumberGenerator.GetBytes(64));
}
}
十三、Refresh Token数据库设计
创建:
RefreshToken.cs
代码:
public class RefreshToken
{
public Guid Id {get;set;}
public Guid UserId {get;set;}
public string TokenHash {get;set;}="";
public DateTime ExpireAt {get;set;}
public bool Revoked {get;set;}
public ApplicationUser User
{get;set;}=null!;
}
十四、为什么数据库保存Hash?
错误:
数据库
refresh_token=abc123
数据库泄露:
攻击者直接登录。
正确:
保存:
Token:
abc123
SHA256:
91AFxxxxx
数据库只有Hash。
十五、Token生成流程
登录:
用户名密码
↓
验证Identity
↓
生成Access Token
↓
生成Refresh Token
↓
Hash Refresh Token
↓
保存数据库
↓
返回客户端
十六、注册API
Controller:
Controllers/AuthController.cs
接口:
POST
/api/auth/register
请求:
{
"email":
"user@test.com",
"password":
"Password@12345"
}
十七、注册逻辑
伪代码:
var user=new ApplicationUser
{
Email=request.Email,
UserName=request.Email
};
var result=
await userManager
.CreateAsync(
user,
password);
if(result.Succeeded)
{
sendVerifyEmail();
}
十八、登录API
接口:
POST
/api/auth/login
请求:
{
"email":
"user@test.com",
"password":
"Password@12345"
}
返回:
{
"accessToken":
"xxxxx",
"refreshToken":
"xxxxx",
"expires":
900
}
十九、JWT Middleware
Program.cs:
增加:
builder.Services
.AddAuthentication()
.AddJwtBearer(options =>
{
options.TokenValidationParameters
=new TokenValidationParameters
{
ValidateIssuer=true,
ValidateAudience=true,
ValidateLifetime=true,
};
});
启用:
app.UseAuthentication();
app.UseAuthorization();
二十、测试保护API
例如:
[Authorize]
[HttpGet]
public IActionResult Profile()
{
return Ok();
}
没有Token:
返回:
401 Unauthorized
二十一、Refresh Token接口
接口:
POST
/api/auth/refresh
流程:
旧Refresh Token
↓
验证Hash
↓
撤销旧Token
↓
生成新Token
↓
返回
二十二、Token轮换机制
禁止:
一个Refresh Token永久有效。
推荐:
Refresh Token:
30天
Access Token:
15分钟
每次刷新:
旧Token:
Revoked=true
生成:
新Token。
二十三、登录设备管理
增加:
UserDevice.cs
字段:
Id
UserId
DeviceName
OS
IP
LastActive
CreatedAt
用途:
以后:
License设备限制。
二十四、RBAC权限基础
角色:
User
Admin
Support
Finance
SuperAdmin
Identity已经支持。
创建:
await roleManager
.CreateAsync(
new IdentityRole<Guid>
("Admin"));
二十五、Admin权限验证
Controller:
[Authorize(
Roles="Admin"
)]
public IActionResult Users()
{
}
二十六、安全增强
生产必须增加:
登录限流
例如:
5分钟:
失败5次
↓
锁定15分钟
密码泄露检测
建议:
接入:
HaveIBeenPwned API
邮箱验证
注册:
状态:
EmailConfirmed=false
未验证:
不能购买License。
二十七、本阶段完成
| 功能 | 状态 |
|---|---|
| ASP.NET Identity | ✅ |
| 用户注册 | ✅ |
| 密码安全 | ✅ |
| JWT认证 | ✅ |
| Refresh Token | ✅ |
| Token轮换 | ✅ |
| 设备记录基础 | ✅ |
| RBAC基础 | ✅ |
| MFA接口预留 | ✅ |
当前系统进度
已经完成:
数据库
↓
Clean Architecture
↓
Identity
↓
JWT认证
下一阶段进入核心商业逻辑:
第九部分(三):License Engine 核心实现
将实现:
- License Key生成算法
- 48D0-61CD-35FE格式生成
- 密钥安全存储
- RSA/ECDSA数字签名
- 激活API
- 设备指纹绑定
- 1台/3台/5台限制
- 在线Heartbeat
- License续期
- 禁用与恢复
- 防爆破设计
这一部分完成后,你的软件就具备真正的“商业激活系统核心”。
LicenseServer 商业授权系统开发实战文档 v1.0
第九部分(三):License Engine 核心实现
本阶段开始实现整个系统最核心的商业能力:
License生成、签名、激活、设备绑定、授权验证。
完成后,你将拥有类似:
- JetBrains License
- Adobe Activation
- Microsoft Product Activation
这类商业授权体系的核心能力。
一、License Engine设计目标
需要解决:
1. License生成
例如:
用户购买:
Professional 版本
3台设备
365天
生成:
48D0-61CD-35FE-9C9B-5358-3725-D644-1A18
2. 防伪
不能:
客户端:
if(key=="xxx")
{
unlock();
}
必须:
服务器:
生成:
License Payload
+
Digital Signature
客户端:
验证:
Signature
3. 设备限制
支持:
例如:
Basic:
1台设备
Professional:
3台设备
Enterprise:
5台/无限设备
4. 防爆破
攻击:
AAAA-BBBB-CCCC
不断尝试
必须:
- Rate Limit
- Hash存储
- IP限制
- 失败记录
二、最终License架构
整体:
Server
|
|
License Generator
|
|
License Database
|
|
RSA/ECDSA Signature
|
|
License Key
|
|
Client SDK
|
|
Signature Verify
三、License数据模型设计
新增:
Domain/Entities
创建:
License.cs
四、License实体
namespace LicenseServer.Domain.Entities;
public class License
{
public Guid Id {get;set;}
/// <summary>
/// 用户
/// </summary>
public Guid UserId {get;set;}
/// <summary>
/// 产品
/// </summary>
public Guid ProductId {get;set;}
/// <summary>
/// License编号
/// </summary>
public string LicenseCode {get;set;}="";
/// <summary>
/// Hash后的Key
/// </summary>
public string LicenseHash {get;set;}="";
/// <summary>
/// 最大设备数量
/// </summary>
public int MaxDevices {get;set;}
/// <summary>
/// 有效期
/// </summary>
public DateTime ExpireAt {get;set;}
/// <summary>
/// 状态
/// </summary>
public LicenseStatus Status {get;set;}
/// <summary>
/// 数字签名
/// </summary>
public string Signature {get;set;}="";
public DateTime CreatedAt {get;set;}
=DateTime.UtcNow;
public User User {get;set;}=null!;
}
五、License状态枚举
创建:
Enums/LicenseStatus.cs
public enum LicenseStatus
{
Active=1,
Suspended=2,
Expired=3,
Revoked=4
}
六、设备绑定模型
创建:
Device.cs
public class Device
{
public Guid Id {get;set;}
public Guid LicenseId {get;set;}
/// <summary>
/// 机器指纹
/// </summary>
public string Fingerprint {get;set;}="";
public string DeviceName {get;set;}="";
public string OS {get;set;}="";
public DateTime ActivatedAt {get;set;}
=DateTime.UtcNow;
public DateTime LastSeenAt {get;set;}
public License License {get;set;}=null!;
}
七、数据库关系
最终:
User
|
|
License
|
|
Device
一个用户:
User A
License:
Professional
Devices:
PC-001
MacBook
Laptop
八、License Key设计
用户看到:
48D0-61CD-35FE-9C9B-5358-3725-D644-1A18
长度:
8组 × 4字符
共:
32字符
九、不要直接存储License
错误:
数据库:
license_code
48D0-61CD...
原因:
数据库泄露:
所有License泄露。
正确:
数据库:
LicenseHash:
SHA256(
license_code
)
十、License生成算法
流程:
Random Bytes
|
|
Base32 Encode
|
|
格式化
|
|
签名
|
|
保存Hash
十一、LicenseGenerator实现
Application:
创建:
Services/LicenseGenerator.cs
代码:
using System.Security.Cryptography;
public class LicenseGenerator
{
public string Generate()
{
var bytes =
RandomNumberGenerator
.GetBytes(20);
var code =
Convert.ToHexString(bytes);
return Format(code);
}
private string Format(string value)
{
value=value[..32];
var groups =
Enumerable.Range(0,8)
.Select(i =>
value.Substring(i*4,4));
return string.Join("-",groups);
}
}
生成:
例如:
输入随机:
48D061CD35FE9C9B53583725D6441A18
输出:
48D0-61CD-35FE-9C9B-5358-3725-D644-1A18
十二、License Hash
创建:
CryptoService.cs
using System.Security.Cryptography;
using System.Text;
public class CryptoService
{
public string SHA256(
string value)
{
using var sha=
SHA256.Create();
var bytes=
sha.ComputeHash(
Encoding.UTF8.GetBytes(value));
return Convert.ToHexString(bytes);
}
}
十三、数字签名设计
这里非常重要。
不要:
AES加密License
原因:
客户端必须解密。
推荐:
非对称密码
使用:
ECDSA P-256
或者:
RSA-3072
推荐:
商业软件:
ECDSA P-256
原因:
- 签名小
- 验证快
- 适合客户端
十四、密钥架构
服务器:
保存:
Private Key
例如:
Vault:
license-sign-private-key
客户端:
内置:
Public Key
关系:
Server Private Key
|
|
Sign
|
|
License
|
|
Client Public Key
|
|
Verify
十五、License Payload设计
签名前:
JSON:
{
"licenseId":
"92cxxx",
"product":
"Professional",
"user":
"user@test.com",
"devices":
3,
"expire":
"2027-01-01"
}
签名:
ECDSA(payload)
生成:
signature
十六、License创建服务
接口:
public interface ILicenseService
{
Task<License>
CreateAsync(
Guid userId,
Guid productId);
Task<bool>
ActivateAsync(
string license,
string fingerprint);
}
十七、创建License流程
管理员:
点击:
Create License
执行:
生成Key
↓
生成Payload
↓
签名
↓
Hash Key
↓
保存数据库
↓
发送邮件
十八、激活API设计
客户端调用:
POST
/api/license/activate
请求:
{
"license":
"48D0-61CD-35FE...",
"fingerprint":
"PC-A82F92XX",
"deviceName":
"DESKTOP-001",
"os":
"Windows 11"
}
返回:
成功:
{
"success":
true,
"license":
{
"expire":
"2027-01-01",
"features":
[
"pro"
]
}
}
失败:
{
"success":
false,
"error":
"DEVICE_LIMIT"
}
十九、激活核心逻辑
伪代码:
public async Task<bool>
ActivateAsync(
string key,
string fingerprint)
{
var hash =
_crypto.Hash(key);
var license =
await db.Licenses
.FirstOrDefaultAsync(
x=>x.LicenseHash==hash);
if(license==null)
return false;
if(license.Status
!=Active)
return false;
var devices =
await db.Devices
.CountAsync(
x=>x.LicenseId
==license.Id);
if(devices>=license.MaxDevices)
return false;
保存设备();
return true;
}
二十、设备重复激活
情况:
用户重新安装:
旧设备:
PC-001
再次激活:
检查:
Fingerprint存在
则:
更新:
LastSeenAt
不增加设备。
二十一、Heartbeat机制
防止:
用户复制License。
客户端:
每天:
POST
/api/license/heartbeat
数据:
{
licenseId:
"xxx",
fingerprint:
"xxx"
}
服务器:
更新:
LastSeenAt
二十二、防爆破设计
激活接口:
必须:
限流
例如:
IP:
一分钟10次
用户:
一天100次
失败记录
表:
LicenseActivationAttempt
字段:
IP
LicenseHash
Time
Success
二十三、Redis缓存
激活查询:
缓存:
license_hash
↓
license info
减少:
PostgreSQL压力。
二十四、客户端验证策略
客户端:
启动:
读取License缓存
↓
验证签名
↓
检查过期
↓
在线Heartbeat
↓
启动软件
二十五、本阶段完成
| 功能 | 状态 |
|---|---|
| License数据模型 | ✅ |
| License Key生成 | ✅ |
| Hash存储 | ✅ |
| ECDSA签名设计 | ✅ |
| 设备绑定 | ✅ |
| 激活API设计 | ✅ |
| 多设备限制 | ✅ |
| Heartbeat设计 | ✅ |
| 防爆破设计 | ✅ |
当前系统能力
现在已经具备:
用户系统
+
License Engine
+
设备限制
+
数字签名
下一阶段:
第九部分(四):客户端 SDK 完整实现
将开始编写:
- C# License SDK
- License文件格式
- 本地加密存储
- ECDSA公钥验证
- 设备指纹生成
- 在线激活客户端
- 离线激活
- Heartbeat
- 自动续期
- Windows/macOS/Linux客户端集成方式
完成后,你的软件端就可以真正接入这个授权系统。
LicenseServer 商业授权系统开发实战文档 v1.0
第九部分(四):客户端 SDK 完整实现
本阶段实现:
给你的桌面软件提供一个商业级 License SDK。
目标:
你的软件开发者只需要:
LicenseManager.Initialize();
var result =
await LicenseManager.ActivateAsync(
licenseKey
);
if(result.Success)
{
StartApplication();
}
即可完成授权。
一、客户端 SDK 设计目标
SDK需要解决:
1. License激活
支持:
在线:
输入License Key
↓
连接服务器
↓
绑定设备
↓
保存授权
2. 本地验证
软件启动:
不能每次联网。
流程:
启动软件
↓
读取本地License
↓
验证数字签名
↓
检查有效期
↓
启动
3. 防破解
客户端不能保存:
明文:
License Key
不能:
license.json
保存:
必须:
加密License Cache
4. 多平台
支持:
Windows
macOS
Linux
.NET:
推荐:
.NET 10
框架:
Class Library
二、SDK工程结构
创建:
client-sdk/
LicenseSDK/
├── LicenseManager.cs
├── LicenseClient.cs
├── DeviceFingerprint.cs
├── LicenseStorage.cs
├── Crypto/
│
├── SignatureVerifier.cs
│
└── HashService.cs
├── Models/
│
├── LicenseInfo.cs
│
└── ActivationResult.cs
├── Exceptions/
└── LicenseException.cs
三、创建SDK项目
根目录:
mkdir client-sdk
cd client-sdk
dotnet new classlib \
-n LicenseSDK
加入Solution:
dotnet sln add \
client-sdk/LicenseSDK
四、SDK目标框架
csproj:
<TargetFrameworks>
net10.0;
net10.0-windows;
net10.0-macos;
net10.0-linux
</TargetFrameworks>
这样:
Windows:
WPF
Avalonia
macOS:
MAUI
Avalonia
Linux:
Avalonia
都可以使用。
五、License数据模型
Models:
创建:
LicenseInfo.cs
代码:
namespace LicenseSDK.Models;
public class LicenseInfo
{
public string LicenseId
{
get;set;
}="";
public string Product
{
get;set;
}="";
public DateTime ExpireAt
{
get;set;
}
public string[] Features
{
get;set;
}=[];
}
六、ActivationResult
创建:
ActivationResult.cs
public class ActivationResult
{
public bool Success
{
get;set;
}
public string Message
{
get;set;
}="";
public LicenseInfo?
License
{
get;set;
}
}
七、设备指纹生成
这是商业授权关键。
目标:
同一台机器:
生成稳定ID。
不推荐
不要:
CPU序列号
硬盘序列号
MAC地址
原因:
- 用户权限不足
- 虚拟机变化
- 隐私问题
推荐组合:
MachineId
+
OS信息
+
随机安装ID
八、本地Machine ID
Windows:
读取:
Registry
HKLM\SOFTWARE\Microsoft\
Cryptography
MachineGuid
Linux:
/etc/machine-id
macOS:
IOPlatformUUID
九、DeviceFingerprint实现
创建:
DeviceFingerprint.cs
代码:
using System.Security.Cryptography;
using System.Text;
namespace LicenseSDK;
public class DeviceFingerprint
{
public static string Generate()
{
var machineId =
GetMachineId();
var raw =
$"{machineId}-{Environment.OSVersion}-{Environment.MachineName}";
using var sha=
SHA256.Create();
var hash=
sha.ComputeHash(
Encoding.UTF8.GetBytes(raw));
return Convert.ToHexString(hash);
}
private static string GetMachineId()
{
return Environment.MachineName;
}
}
生产版本:
这里需要分别实现:
WindowsProvider
LinuxProvider
MacProvider
十、License本地存储
目录:
Windows:
%ProgramData%/Company/App/license
macOS:
~/Library/Application Support/App/license
Linux:
~/.config/app/license
十一、为什么不用Registry?
因为:
注册表:
容易:
导出
修改
删除
推荐:
加密文件。
十二、LicenseStorage
创建:
LicenseStorage.cs
代码:
public class LicenseStorage
{
private readonly string path;
public LicenseStorage()
{
path=
GetPath();
}
public void Save(
string data)
{
File.WriteAllText(
path,
data);
}
public string?
Load()
{
if(!File.Exists(path))
return null;
return File.ReadAllText(path);
}
}
十三、本地加密
Windows:
使用:
DPAPI
macOS:
使用:
Keychain
Linux:
使用:
Secret Service API
统一:
SDK:
IStorageEncryption
接口:
public interface IStorageEncryption
{
string Encrypt(
string data);
string Decrypt(
string data);
}
十四、ECDSA签名验证
服务器:
私钥:
Private Key
客户端:
公钥:
Public Key
创建:
SignatureVerifier.cs
代码:
using System.Security.Cryptography;
public class SignatureVerifier
{
public bool Verify(
string payload,
string signature)
{
using var ecdsa=
ECDsa.Create();
ecdsa.ImportSubjectPublicKeyInfo(
Convert.FromBase64String(
PublicKey),
out _);
return ecdsa.VerifyData(
Encoding.UTF8.GetBytes(payload),
Convert.FromBase64String(signature),
HashAlgorithmName.SHA256
);
}
private const string PublicKey=
"SERVER_PUBLIC_KEY";
}
十五、LicenseClient
负责网络通信。
创建:
LicenseClient.cs
依赖:
HttpClient。
代码:
public class LicenseClient
{
private readonly HttpClient client;
public LicenseClient(
HttpClient client)
{
this.client=client;
}
public async Task<
ActivationResult>
ActivateAsync(
string key,
string fingerprint)
{
var response=
await client.PostAsJsonAsync(
"/api/license/activate",
new
{
license=key,
fingerprint
});
return await response
.Content
.ReadFromJsonAsync
<ActivationResult>();
}
}
十六、LicenseManager核心
这是用户调用入口。
创建:
LicenseManager.cs
代码:
public class LicenseManager
{
private readonly LicenseClient client;
private readonly LicenseStorage storage;
public async Task<bool>
ActivateAsync(
string key)
{
var fingerprint=
DeviceFingerprint.Generate();
var result=
await client.ActivateAsync(
key,
fingerprint);
if(!result.Success)
return false;
storage.Save(
JsonSerializer.Serialize(
result.License));
return true;
}
public bool ValidateLocal()
{
var license=
storage.Load();
if(license==null)
return false;
return true;
}
}
十七、软件集成方式
你的软件:
例如:
PhotoEditor.exe
启动:
static async Task Main()
{
var license=
new LicenseManager();
if(!license.ValidateLocal())
{
Console.WriteLine(
"需要激活");
return;
}
StartApp();
}
十八、在线激活流程
完整流程:
用户输入Key
↓
SDK生成Fingerprint
↓
POST /activate
↓
服务器验证
↓
设备绑定
↓
返回License Payload
↓
验证Signature
↓
加密保存
↓
启动软件
十九、离线激活设计
商业软件必须支持。
流程:
用户:
生成:
Request File
machine.req
发送给你。
服务器:
生成:
license.dat
用户导入。
二十、离线License格式
例如:
{
payload:
{
product:"Pro",
expire:"2027"
},
signature:
"xxxxx"
}
客户端:
只验证:
Signature
不需要联网。
二十一、Heartbeat机制
在线软件:
启动:
每24小时
调用:
/api/license/heartbeat
发送:
{
licenseId,
fingerprint
}
二十二、SDK防破解增强
商业版增加:
1. 混淆
.NET:
推荐:
- Eazfuscator.NET
- Dotfuscator
2. 关键代码拆分
不要:
if(valid)
unlock();
改:
LicenseService
↓
FeatureService
↓
RuntimeCheck
3. 内存保护
避免:
明文:
License Key
4. Anti Debug
检测:
- Debugger
- Dump
- Patch
二十三、SDK NuGet发布
最终:
发布:
Company.License.SDK
使用:
<PackageReference
Include="Company.License.SDK"
Version="1.0.0"
/>
二十四、本阶段完成
| 功能 | 状态 |
|---|---|
| SDK架构 | ✅ |
| License激活 | ✅ |
| 设备指纹 | ✅ |
| 本地缓存 | ✅ |
| 签名验证 | ✅ |
| 在线授权 | ✅ |
| 离线授权设计 | ✅ |
| Heartbeat设计 | ✅ |
| NuGet发布方案 | ✅ |
当前系统能力
现在已经形成:
用户系统
+
License Server
+
License Engine
+
Client SDK
=
完整商业授权平台
下一阶段:
第九部分(五):生产级安全加固
将进入商业软件真正上线前最重要部分:
内容:
- 私钥安全管理(Vault/HSM)
- License密钥防泄露
- API防攻击
- 防爆破体系
- WAF设计
- 数据库安全
- 审计日志
- 管理员权限隔离
- 客户端反调试
- 代码混淆策略
- 安全测试流程
- 商业软件漏洞响应机制
这一部分完成后,系统安全等级才达到真正商业发行标准。
LicenseServer 商业授权系统开发实战文档 v1.0
第九部分(五):生产级安全加固
本阶段进入商业发行前最后一个关键环节:
将一个“能工作的授权系统”,提升到“可以卖给客户的商业授权平台”。
目标:
完成:
✅ 密钥安全体系 ✅ 服务端防攻击 ✅ API安全 ✅ 数据库安全 ✅ 管理后台安全 ✅ 审计体系 ✅ 客户端防破解增强 ✅ 安全测试流程
一、安全架构重新设计
生产环境不能是:
id="old-security"
API Server
|
Database
|
Private Key
原因:
如果服务器被入侵:
攻击者可能获得:
- 数据库
- License生成能力
- 私钥
生产架构:
id="secure-architecture"
Internet
|
Cloudflare WAF
|
Nginx Gateway
|
ASP.NET Core API
/ \
/ \
PostgreSQL Vault
|
|
License Private Key
二、核心原则:私钥永远不能暴露
这是整个系统最重要原则。
错误设计
例如:
id="bad-private-key"
var privateKey=
"MIIC....";
然后:
发布到服务器。
风险:
- Git泄露
- 服务器泄露
- 运维人员读取
三、正确方案:HashiCorp Vault
架构:
id="vault-flow"
License API
|
|
Vault Authentication
|
|
Private Key
API:
需要签名时:
请求:
id="vault-request"
Generate License Signature
Vault:
返回:
id="vault-response"
Signature
API:
不知道私钥。
四、Vault部署
Docker:
id="vault-docker"
services:
vault:
image:
hashicorp/vault:latest
container_name:
license-vault
cap_add:
- IPC_LOCK
ports:
- "8200:8200"
启动:
id="vault-start"
docker compose up -d vault
初始化:
id="vault-init"
vault operator init
保存:
id="vault-key"
Unseal Keys
Root Token
注意:
不要保存:
- Git
- 文档
- 网盘
推荐:
密码管理器。
五、密钥分类管理
生产环境:
至少:
id="key-level"
Vault
├── License Signing Key
├── JWT Secret
├── Database Password
├── SMTP Password
└── Payment Secret
六、License签名密钥升级
推荐:
ECDSA P-256
密钥:
id="ecdsa"
Private Key
Server Only
Public Key
Client SDK
七、密钥轮换机制
商业系统必须考虑:
如果私钥泄露怎么办?
设计:
Key Version。
例如:
id="key-version"
{
"keyId":
"2026-v1",
"algorithm":
"ECDSA-P256"
}
客户端:
保存:
id="multi-key"
PublicKey-v1
PublicKey-v2
升级:
不用重新发布软件。
八、API安全加固
1. HTTPS强制
生产:
只允许:
id="tls"
TLS 1.3
禁止:
TLS 1.0
TLS 1.1
Nginx:
id="nginx-tls"
ssl_protocols TLSv1.3;
九、API Rate Limit
重点接口:
激活接口
攻击:
id="attack"
POST /license/activate
10000次/分钟
限制:
普通用户:
id="limit"
10 requests/min
IP:
id="ip-limit"
60 requests/min
使用:
ASP.NET Core RateLimiter。
安装:
无需第三方。
Program.cs:
id="rate-limit"
builder.Services
.AddRateLimiter(options =>
{
options.AddFixedWindowLimiter(
"license",
config =>
{
config.PermitLimit=10;
config.Window=
TimeSpan.FromMinutes(1);
});
});
启用:
id="enable-rate"
app.UseRateLimiter();
十、License爆破防护
攻击:
尝试:
AAAA-BBBB-CCCC
AAAA-BBBB-CCCD
AAAA-BBBB-CCCE
防护:
1. 不保存明文Key
数据库:
id="hash-only"
SHA256(Key)
2. 延迟响应
失败:
第一次:
100ms
连续失败:
5s
3. IP黑名单
Redis:
保存:
id="redis-ban"
failed:
IP
count
expire
十一、数据库安全
PostgreSQL配置
禁止:
公网访问。
错误:
id="wrong-db"
0.0.0.0:5432
正确:
id="right-db"
API Container
|
|
PostgreSQL Network
十二、数据库账号权限
不要:
id="bad-db-user"
postgres
生产:
创建:
id="db-user"
license_api
权限:
只有:
SELECT
INSERT
UPDATE
DELETE
没有:
CREATE DATABASE
SUPERUSER
十三、数据库加密
敏感字段:
例如:
邮箱:
可选择:
应用层加密。
结构:
id="encrypt-field"
Email
|
AES-256
|
Database
十四、Admin后台安全
Admin是最高风险区域。
必须:
MFA
管理员登录:
密码:
TOTP。
例如:
Google Authenticator。
IP限制
例如:
管理员:
只允许:
id="admin-ip"
公司IP
VPN IP
操作确认
危险操作:
例如:
删除License。
要求:
二次确认。
十五、RBAC权限细化
不要只有:
id="simple-role"
Admin
生产:
id="roles"
SuperAdmin
|
LicenseManager
|
Support
|
Finance
|
ReadOnly
权限表:
id="permission"
Role
Permission
例如:
Support:
允许:
查看用户
查看License
禁止:
生成License
退款
十六、审计日志系统
商业软件必须有。
记录:
id="audit"
谁
什么时候
什么IP
做了什么
结果
例如:
id="audit-json"
{
"user":
"admin@test.com",
"action":
"CREATE_LICENSE",
"time":
"2026-08-11",
"ip":
"1.2.3.4"
}
数据库:
id="audit-table"
AuditLogs
Id
UserId
Action
IPAddress
CreatedAt
十七、日志安全
不要记录:
禁止:
id="no-log"
License Key
Password
Token
可以:
id="yes-log"
LicenseId
UserId
Result
十八、客户端安全增强
服务端安全之后:
客户端也是重点。
十九、代码混淆
.NET推荐:
商业:
- Eazfuscator.NET
- Babel Obfuscator
免费:
- ConfuserEx
作用:
保护:
id="protect"
License逻辑
算法
API地址
二十、防调试
检测:
id="debug"
Debugger.IsAttached
但是:
不要作为唯一保护。
原因:
容易绕过。
二十一、关键逻辑拆分
不要:
id="bad-check"
if(valid)
{
unlock();
}
改:
id="split"
LicenseValidator
|
FeatureManager
|
RuntimeGuard
|
Application
二十二、本地License保护
保存:
不要:
id="plain"
license.json
改:
id="secure"
license.dat
AES-256-GCM
密钥:
Windows:
DPAPI
macOS:
Keychain
Linux:
Secret Service
二十三、防时间回拨
攻击:
修改系统时间。
解决:
保存:
id="time"
LastServerTime
LastLocalTime
检测:
id="rollback"
current < previous
判定异常。
二十四、离线授权安全
离线License:
必须:
包含:
id="offline"
{
LicenseId,
MachineFingerprint,
Expire,
Signature
}
不能:
只保存:
id="bad-offline"
Expire=2099
二十五、安全测试流程
上线前:
必须测试。
1. API扫描
工具:
- OWASP ZAP
- Burp Suite
检查:
- SQL Injection
- XSS
- CSRF
- JWT漏洞
2. 依赖扫描
.NET:
id="dotnet-audit"
dotnet list package
--vulnerable
Node:
id="npm-audit"
bun audit
3. Docker扫描
Trivy:
id="trivy"
trivy image license-api
二十六、备份策略
数据库:
每天:
id="backup"
Full Backup
保留:
id="retain"
7天
30天
90天
二十七、灾难恢复
必须测试:
不是:
“有备份”。
而是:
id="restore"
新服务器
↓
恢复数据库
↓
恢复Vault
↓
启动服务
↓
验证License
二十八、生产安全Checklist
上线前:
Server
✅ HTTPS
✅ Firewall
✅ SSH Key登录
✅ 禁止root登录
API
✅ JWT
✅ Rate Limit
✅ Audit Log
✅ Input Validation
Database
✅ 私网
✅ 最小权限
✅ 自动备份
License
✅ Hash存储
✅ ECDSA签名
✅ Key轮换
Client
✅ 混淆
✅ 本地加密
✅ 防时间回拨
二十九、本阶段完成
| 模块 | 完成 |
|---|---|
| Vault密钥管理 | ✅ |
| ECDSA密钥体系 | ✅ |
| API防护 | ✅ |
| 爆破防护 | ✅ |
| 数据库安全 | ✅ |
| Admin安全 | ✅ |
| 审计系统 | ✅ |
| 客户端增强 | ✅ |
| 安全测试流程 | ✅ |
当前系统等级
现在:
普通授权系统
↓
商业级License平台
↓
企业软件授权架构
下一阶段:
第九部分(六):支付系统 + 自动发货 + 商业闭环实现
将实现:
- 产品套餐设计
- Stripe/Paddle支付接入
- Webhook安全验证
- 支付成功自动生成License
- 邮件自动发送
- 订单系统
- 退款处理
- 订阅续费
- License升级降级
- 商业运营后台
完成这一部分后,整个系统将从“技术平台”变成真正可以销售的软件商业系统。
LicenseServer 商业授权系统开发实战文档 v1.0
第九部分(六):支付系统 + 自动发货 + 商业闭环实现
本阶段开始把 LicenseServer 从:
“技术授权平台”
升级为:
“可直接销售软件的商业闭环系统”。
完成后流程:
用户访问官网
↓
选择软件版本
↓
在线支付
↓
支付平台确认
↓
自动创建订单
↓
自动生成License
↓
邮件发送激活码
↓
用户下载安装
↓
激活软件
↓
续费 / 升级 / 售后
一、商业模式设计
首先设计产品模型。
不要把License和支付直接绑定。
正确:
Product
产品
↓
Plan
套餐
↓
Order
订单
↓
License
授权
关系:
Product
|
|
Plan
|
|
Order
|
|
License
二、产品模型设计
例如:
软件:
PhotoManager Pro
套餐:
| 套餐 | 价格 | 设备 |
|---|---|---|
| Personal | $29 | 1台 |
| Professional | $79 | 3台 |
| Business | $199 | 5台 |
| Enterprise | 联系销售 | 无限 |
三、数据库设计扩展
新增:
Order
Payment
Product
Plan
Invoice
Subscription
Coupon
四、Product实体
Domain:
创建:
Entities/Product.cs
public class Product
{
public Guid Id {get;set;}
public string Name {get;set;}="";
public string Description {get;set;}="";
public bool Active {get;set;}=true;
public DateTime CreatedAt {get;set;}
=DateTime.UtcNow;
}
五、Pricing Plan实体
创建:
Plan.cs
public class Plan
{
public Guid Id {get;set;}
public Guid ProductId {get;set;}
public string Name {get;set;}="";
public decimal Price {get;set;}
public string Currency {get;set;}
="USD";
public int MaxDevices {get;set;}
public int ValidDays {get;set;}
public Product Product {get;set;}=null!;
}
六、Order订单模型
创建:
Order.cs
public class Order
{
public Guid Id {get;set;}
public Guid UserId {get;set;}
public string OrderNo {get;set;}="";
public decimal Amount {get;set;}
public string Currency {get;set;}
="USD";
public OrderStatus Status {get;set;}
public string PaymentProvider {get;set;}="";
public string PaymentId {get;set;}="";
public DateTime CreatedAt {get;set;}
=DateTime.UtcNow;
}
七、订单状态
public enum OrderStatus
{
Pending=0,
Paid=1,
Failed=2,
Refunded=3,
Cancelled=4
}
八、支付架构设计
推荐支持:
第一阶段
Stripe
适合:
- 欧美客户
- SaaS
- 国际支付
第二阶段
Paddle
优势:
Merchant of Record。
处理:
- VAT
- 税务
- 发票
国内:
可以增加:
- 支付宝
- 微信支付
九、支付流程
以Stripe为例:
用户点击购买
↓
创建Order
↓
创建Stripe Checkout Session
↓
跳转支付页面
↓
用户付款
↓
Stripe Webhook
↓
验证签名
↓
更新Order Paid
↓
生成License
↓
发送邮件
十、不要相信前端支付结果
错误:
if(paymentSuccess)
{
createLicense();
}
原因:
用户可以修改JS。
正确:
只相信:
Payment Provider Webhook
十一、Stripe SDK安装
Backend:
dotnet add package Stripe.net
十二、Stripe配置
环境变量:
STRIPE_SECRET_KEY
STRIPE_WEBHOOK_SECRET
不要:
写入:
appsettings.json
十三、Payment Service设计
Application:
创建:
IPaymentService.cs
public interface IPaymentService
{
Task<string>
CreateCheckoutAsync(
Guid orderId);
Task HandleWebhookAsync(
string json,
string signature);
}
十四、创建支付订单
流程:
用户购买:
Professional
服务器:
创建:
{
orderId:
"xxxx",
amount:
79,
currency:
"usd"
}
Stripe:
返回:
checkout_url
用户跳转:
stripe.com/payment/xxx
十五、Webhook处理
接口:
POST
/api/payment/webhook
接收:
Stripe事件。
例如:
{
"type":
"checkout.session.completed"
}
十六、Webhook安全验证
必须:
验证:
Stripe-Signature
不能:
直接:
PaymentSuccess();
流程:
Webhook
↓
验证Signature
↓
确认Event
↓
检查Order
↓
更新状态
十七、订单支付完成
成功:
数据库:
Order:
Status=Paid
然后:
进入:
License生成队列。
十八、自动发货架构
不要:
支付接口里直接生成。
错误:
Webhook
↓
Generate License
↓
Send Email
原因:
Webhook可能超时。
正确:
消息队列:
Webhook
↓
Order Paid Event
↓
Queue
↓
Worker
↓
Generate License
↓
Send Email
十九、后台任务Worker
.NET:
使用:
BackgroundService
结构:
LicenseServer.Worker
负责:
- 发License
- 邮件
- 清理任务
二十、支付事件
定义:
public class OrderPaidEvent
{
public Guid OrderId {get;set;}
}
二十一、License自动生成
Worker:
读取Order
↓
获取Plan
↓
CreateLicense()
↓
保存
↓
绑定User
↓
发送邮件
二十二、邮件系统
推荐:
生产:
- Amazon SES
- SendGrid
- Mailgun
开发:
Mailhog。
二十三、邮件模板
购买成功:
感谢购买。
订单号:
XXXX
License:
XXXX-XXXX-XXXX
下载地址:
https://xxx.com/download
二十四、邮件安全
不要发送:
管理员密码。
License邮件:
建议:
发送:
License ID
下载链接
激活说明
完整Key:
可选择:
显示一次。
二十五、License找回系统
用户:
登录网站。
进入:
My Licenses
查看:
购买记录
License状态
设备数量
二十六、找回机制
不要:
通过邮箱直接返回:
License Key。
原因:
邮箱泄露风险。
推荐:
登录账户:
查看。
二十七、退款流程
流程:
Customer Request Refund
↓
Admin Review
↓
Payment Refund
↓
Webhook
↓
License Revoked
二十八、退款后授权处理
状态:
License
Active
↓
Suspended
↓
Revoked
客户端:
Heartbeat:
发现:
REVOKED
停止运行。
二十九、订阅模式设计
未来支持:
月付:
$9/month
年付:
$79/year
新增:
Subscription。
三十、Subscription模型
public class Subscription
{
public Guid Id {get;set;}
public Guid UserId {get;set;}
public string ProviderId {get;set;}="";
public DateTime StartAt {get;set;}
public DateTime NextBillingAt {get;set;}
public bool Active {get;set;}
}
三十一、License升级
例如:
用户:
Personal
升级:
Professional。
计算:
Professional价格
-
已经支付金额
生成:
新License。
旧:
Expired
三十二、优惠券系统
数据库:
Coupon:
Code
Discount
Expire
MaxUsage
三十三、商业后台增加
Admin:
增加菜单:
销售管理
订单管理
支付记录
退款
优惠券
收入统计
三十四、财务统计
统计:
Daily Revenue
Monthly Revenue
Active Users
Conversion Rate
Refund Rate
三十五、完整商业闭环
最终:
访问官网
↓
注册账户
↓
选择套餐
↓
支付
↓
Webhook确认
↓
Order Paid
↓
Worker
↓
生成License
↓
邮件发送
↓
客户端下载
↓
激活
↓
设备管理
↓
续费升级
↓
收入统计
三十六、本阶段完成
| 模块 | 状态 |
|---|---|
| Product设计 | ✅ |
| Plan套餐 | ✅ |
| Order系统 | ✅ |
| Stripe支付 | ✅ |
| Webhook安全 | ✅ |
| 自动发货 | ✅ |
| 邮件系统 | ✅ |
| 退款流程 | ✅ |
| 订阅设计 | ✅ |
| 商业后台需求 | ✅ |
当前系统完整度
目前已经完成:
用户系统
+
License Engine
+
Client SDK
+
安全体系
+
支付闭环
=
商业授权平台
下一阶段:
第九部分(七):Admin Console 完整实现
将开发:
- Next.js Admin架构
- 管理员登录
- RBAC权限管理
- 用户管理
- License管理
- 设备管理
- 订单管理
- 支付管理
- 审计日志查看
- 数据统计Dashboard
完成后,你将拥有完整的商业运营后台。
LicenseServer 商业授权系统开发实战文档 v1.0
第九部分(七):Admin Console 完整实现
本阶段实现:
商业授权平台管理员控制中心(Admin Console)
目标:
让运营人员无需访问数据库,通过Web后台完成:
- 用户管理
- License管理
- 产品管理
- 订单管理
- 支付管理
- 设备管理
- 权限管理
- 审计查看
- 数据分析
最终架构:
Admin User
|
|
Next.js Admin Console
|
|
ASP.NET Core API
|
+-----------+-----------+
| |
PostgreSQL Redis
一、技术选型
Admin Console:
推荐:
| 功能 | 技术 |
|---|---|
| 前端框架 | Next.js 16 |
| 语言 | TypeScript |
| UI | shadcn/ui |
| 样式 | Tailwind CSS |
| 数据请求 | TanStack Query |
| 表格 | TanStack Table |
| 图表 | Recharts |
| 认证 | JWT + HttpOnly Cookie |
理由:
和商业SaaS后台技术路线一致。
二、工程结构
创建:
admin-console/
├── app/
│
├── login/
│
├── dashboard/
├── users/
├── licenses/
├── orders/
├── products/
├── audit/
├── settings/
├── components/
├── lib/
├── hooks/
├── types/
├── middleware.ts
└── package.json
三、创建Next.js项目
环境:
Node:
推荐:
Node.js 24 LTS
或者:
Bun 1.2+
创建:
bun create next-app admin-console
选择:
✔ TypeScript
✔ App Router
✔ Tailwind CSS
✔ ESLint
四、安装依赖
bun add \
@tanstack/react-query \
axios \
zod \
react-hook-form
UI:
bunx shadcn@latest init
添加:
bunx shadcn@latest add \
button \
card \
table \
dialog \
form \
dropdown-menu
五、目录设计
推荐:
app
├── (auth)
│
└── login
├── (admin)
│
├── dashboard
├── users
├── licenses
├── orders
components
lib
services
types
六、管理员登录设计
流程:
Admin
↓
输入账号密码
↓
API验证
↓
生成JWT
↓
HttpOnly Cookie
↓
进入后台
七、为什么不用LocalStorage保存Token?
错误:
localStorage.setItem(
"token",
jwt
)
风险:
XSS可以读取。
推荐:
Cookie:
Set-Cookie:
access_token=xxx;
HttpOnly;
Secure;
SameSite=Strict
八、登录页面
路径:
/login
结构:
+----------------+
Email
Password
[ Login ]
+----------------+
组件:
<LoginForm />
九、API Client封装
创建:
lib/api.ts
代码:
import axios from "axios";
export const api =
axios.create({
baseURL:
process.env
.NEXT_PUBLIC_API_URL,
withCredentials:true
});
十、Dashboard主页
路径:
/dashboard
显示:
核心指标:
Users
Licenses
Orders
Revenue
Active Devices
页面:
+-----------------------+
Users 12000
Licenses 9800
Revenue $24000
Active 8500
+-----------------------+
十一、Dashboard API
Backend:
增加:
GET
/api/admin/dashboard
返回:
{
users:12000,
licenses:9800,
revenue:24000,
devices:8500
}
十二、用户管理
路径:
/users
功能:
- 搜索用户
- 查看详情
- 禁用账户
- 重置密码
- 查看License
列表:
| 状态 | 注册时间 | |
|---|---|---|
| test@gmail.com | Active | 2026-01 |
十三、用户详情页
路径:
/users/{id}
显示:
用户信息
Email
Created
Last Login
Licenses
Devices
Orders
十四、License管理
路径:
/licenses
功能:
查看
字段:
| 字段 |
|---|
| License ID |
| 用户 |
| 产品 |
| 状态 |
| 设备数量 |
| 过期时间 |
操作
管理员可以:
- 创建License
- 禁用License
- 恢复License
- 删除测试License
十五、创建License页面
表单:
用户:
[选择]
产品:
[Professional]
设备数量:
[3]
有效期:
[365天]
[Create]
调用:
POST
/api/admin/licenses
十六、License详情
显示:
License:
48D0-61CD...
Status:
Active
Devices:
2/3
Created:
2026
设备:
PC-001
Windows11
Last Seen
2026-08-10
MacBook
macOS
2026-08-09
十七、设备管理
路径:
/devices
功能:
- 查看设备
- 强制解绑
- 查看IP
- 查看最后在线时间
十八、订单管理
路径:
/orders
显示:
| 订单 | 金额 | 状态 |
|---|---|---|
| ORD001 | 79 USD | Paid |
操作:
- 查看支付信息
- 退款
- 重新发送License
十九、支付管理
显示:
Stripe Payment ID
Amount
Currency
Created
Status
二十、产品管理
路径:
/products
管理:
产品:
PhotoManager Pro
套餐:
Personal
Professional
Enterprise
二十一、RBAC权限系统
后台用户:
不要只有Admin。
设计:
SuperAdmin
|
Admin
|
Support
|
Finance
|
Viewer
二十二、权限数据库
新增:
AdminUser
Role
Permission
RolePermission
例如:
Support:
允许:
查看用户
查看License
禁止:
生成License
退款
二十三、前端权限控制
例如:
<Permission
name="license.create"
>
<CreateButton />
</Permission>
二十四、审计日志页面
路径:
/audit
显示:
时间
管理员
IP
操作
结果
例如:
2026-08-10
admin
CREATE_LICENSE
SUCCESS
二十五、安全设置页面
路径:
/settings/security
功能:
- 修改密码
- MFA绑定
- 登录设备
- API Key管理
二十六、管理员MFA
流程:
输入密码
↓
TOTP验证
↓
进入后台
数据库:
增加:
TwoFactorEnabled
SecretKey
二十七、文件上传安全
后台上传:
例如:
软件安装包。
必须:
限制:
文件类型
文件大小
病毒扫描
二十八、Dashboard数据图表
推荐:
Recharts。
显示:
收入趋势
Jan
Feb
Mar
Apr
License增长
New License
Active License
Expired
二十九、后台API安全
所有:
/api/admin/*
必须:
[Authorize(
Roles="Admin"
)]
进一步:
Permission:
[RequirePermission(
"license.delete"
)]
三十、Docker部署
Admin Console:
Dockerfile:
FROM node:24-alpine
WORKDIR /app
COPY . .
RUN npm install
RUN npm run build
CMD [
"npm",
"start"
]
三十一、docker-compose
admin:
image:
license-admin
ports:
- "3000:3000"
environment:
NEXT_PUBLIC_API_URL:
https://api.example.com
三十二、CI/CD
GitHub Actions:
流程:
Push
↓
Build
↓
Test
↓
Docker Build
↓
Push Registry
↓
Deploy
三十三、生产部署建议
推荐:
结构:
Cloudflare
|
Nginx
|
Next.js Admin
|
ASP.NET API
|
PostgreSQL
三十四、后台性能优化
数据列表:
不要一次加载:
100000用户。
使用:
分页:
GET
/users?page=1&pageSize=50
搜索:
使用:
PostgreSQL:
GIN Index
三十五、缓存策略
Dashboard:
Redis:
缓存:
dashboard_stats
60秒
三十六、本阶段完成
| 模块 | 状态 |
|---|---|
| Next.js Admin | ✅ |
| 管理员登录 | ✅ |
| JWT Cookie认证 | ✅ |
| Dashboard | ✅ |
| 用户管理 | ✅ |
| License管理 | ✅ |
| 订单管理 | ✅ |
| 支付管理 | ✅ |
| RBAC | ✅ |
| 审计日志 | ✅ |
| MFA设计 | ✅ |
| Docker部署 | ✅ |
当前系统完成度
目前已经拥有:
官网
|
支付系统
|
License Server
|
Client SDK
|
Admin Console
=
完整商业软件授权生态
下一阶段:
第九部分(八):生产部署上线方案
将进入最终上线阶段:
内容:
- 云服务器架构设计
- Docker Compose生产部署
- PostgreSQL高可用
- Redis部署
- Nginx反向代理
- HTTPS自动续期
- Cloudflare配置
- CI/CD自动发布
- 监控告警
- 日志系统
- 备份恢复
- 灰度升级方案
完成后,这套系统即可正式商业运营。
LicenseServer 商业授权系统开发实战文档 v1.0
第九部分(八):生产部署上线方案
本阶段进入:
从开发环境 → 商业生产环境
目标:
部署一套:
- 高安全
- 可维护
- 可扩展
- 可监控
- 可备份
的正式 License SaaS 平台。
一、生产环境总体架构
推荐架构:
用户
|
|
Cloudflare CDN
|
|
WAF 防火墙
|
|
Nginx Gateway
|
+---------------+----------------+
| |
| |
Admin Console API Server
Next.js ASP.NET Core
|
+----------------+---------------+
| |
PostgreSQL Redis
|
|
Backup Storage
|
|
Vault
密钥管理系统
二、服务器规划建议
小型商业初期
适合:
用户:
< 10万
配置:
API服务器
CPU:
4 Core
RAM:
8GB
SSD:
80GB
运行:
- ASP.NET API
- Worker
数据库服务器
CPU:
4 Core
RAM:
16GB
SSD:
200GB
运行:
- PostgreSQL
Redis
RAM:
2GB
三、推荐云厂商
国际:
- AWS
- Azure
- Google Cloud
- DigitalOcean
- Hetzner
国内:
- 阿里云
- 腾讯云
- 华为云
个人商业软件:
推荐:
初期:
Hetzner VPS
+
Cloudflare
成本低。
四、Linux系统选择
推荐:
Ubuntu Server 24.04 LTS
原因:
- 社区最大
- Docker支持好
- 安全更新长期支持
或者:
Debian 13。
五、服务器初始化
登录:
ssh root@server_ip
更新:
apt update
apt upgrade -y
安装基础工具:
apt install \
curl \
git \
vim \
ufw \
fail2ban \
htop \
net-tools
六、安全初始化
禁止root SSH登录
编辑:
vim /etc/ssh/sshd_config
修改:
PermitRootLogin no
创建管理员:
adduser deploy
usermod -aG sudo deploy
重启SSH:
systemctl restart ssh
七、防火墙配置
安装:
ufw enable
允许:
SSH:
ufw allow 22
HTTP:
ufw allow 80
HTTPS:
ufw allow 443
查看:
ufw status
八、安装Docker
官方方式:
curl -fsSL https://get.docker.com \
| sh
检查:
docker version
安装Compose:
docker compose version
九、生产目录规划
推荐:
/opt/license-server/
├── api/
├── admin/
├── worker/
├── postgres/
├── redis/
├── nginx/
├── vault/
├── backups/
└── docker-compose.yml
十、生产Docker Compose
结构:
docker-compose.yml
包含:
- api
- admin
- worker
- postgres
- redis
- nginx
示例:
version: "3.9"
services:
postgres:
image:
postgres:17
restart:
always
environment:
POSTGRES_DB:
license
POSTGRES_USER:
license_api
POSTGRES_PASSWORD:
${DB_PASSWORD}
volumes:
- ./postgres:/var/lib/postgresql/data
redis:
image:
redis:7-alpine
restart:
always
api:
image:
company/license-api:latest
restart:
always
depends_on:
- postgres
- redis
worker:
image:
company/license-worker:latest
restart:
always
admin:
image:
company/license-admin:latest
restart:
always
nginx:
image:
nginx:latest
ports:
- "80:80"
- "443:443"
十一、环境变量管理
不要:
appsettings.json
生产:
使用:
.env
例如:
DB_PASSWORD=
JWT_SECRET=
STRIPE_SECRET=
SMTP_PASSWORD=
VAULT_TOKEN=
权限:
chmod 600 .env
十二、PostgreSQL生产配置
参数优化
postgresql.conf:
内存:
例如:
16GB RAM:
shared_buffers = 4GB
effective_cache_size = 12GB
maintenance_work_mem = 1GB
十三、数据库索引
必须建立:
License:
CREATE INDEX idx_license_hash
ON licenses(
license_hash
);
Order:
CREATE INDEX idx_order_no
ON orders(
order_no
);
User:
CREATE INDEX idx_user_email
ON users(
email
);
十四、数据库备份
使用:
pg_dump。
脚本:
#!/bin/bash
DATE=$(date +%Y%m%d)
pg_dump license \
> backup-$DATE.sql
自动:
cron:
每天:
3点。
0 3 * * *
/opt/scripts/backup.sh
十五、备份上传
推荐:
对象存储:
- AWS S3
- Cloudflare R2
- 阿里云OSS
流程:
PostgreSQL
↓
Backup
↓
Encrypt
↓
Cloud Storage
十六、Redis部署
用途:
缓存:
license查询
rate limit
session
queue
生产:
开启密码:
redis.conf:
requirepass StrongPassword
十七、Nginx反向代理
结构:
license.example.com
|
Nginx
|
localhost:5000
配置:
server {
server_name api.example.com;
location / {
proxy_pass
http://api:8080;
proxy_set_header
Host $host;
proxy_set_header
X-Real-IP $remote_addr;
}
}
十八、HTTPS证书
推荐:
Let’s Encrypt。
安装:
apt install certbot
申请:
certbot --nginx \
-d api.example.com
自动续期:
测试:
certbot renew --dry-run
十九、Cloudflare配置
DNS:
api.example.com
A
server_ip
开启:
Proxy ON
安全:
开启:
- Bot Fight Mode
- WAF Rules
- Rate Limiting
二十、CI/CD自动部署
推荐:
GitHub Actions。
流程:
git push
↓
GitHub Actions
↓
dotnet test
↓
docker build
↓
docker push
↓
服务器pull
↓
docker compose restart
二十一、GitHub Actions示例
name:
Deploy
on:
push:
branches:
- main
jobs:
build:
runs-on:
ubuntu-latest
steps:
- uses:
actions/checkout@v4
- name:
Build Docker
run:
docker build .
二十二、日志系统
生产不要:
SSH看日志。
推荐:
方案:
Loki + Grafana
结构:
Containers
|
Promtail
|
Loki
|
Grafana
查看:
- API错误
- 激活失败
- 支付失败
- 登录异常
二十三、监控系统
推荐:
Prometheus + Grafana。
监控:
Server
- CPU
- RAM
- Disk
API
- 请求数量
- 延迟
- 错误率
License
- 激活次数
- 失败次数
- 爆破次数
二十四、告警系统
例如:
邮件:
CPU >90%
↓
发送邮件
License:
5分钟失败激活 >100
↓
安全告警
二十五、应用健康检查
API:
增加:
GET
/health
返回:
{
"status":
"healthy"
}
Docker:
healthcheck:
healthcheck:
test:
curl localhost:8080/health
二十六、版本发布策略
不要:
直接覆盖。
采用:
版本:
v1.0.0
v1.0.1
v1.1.0
Docker:
license-api:v1.2.0
二十七、数据库迁移
使用:
EF Core Migration。
发布:
dotnet ef database update
生产:
部署前:
执行:
Migration
↓
Backup
↓
Deploy
二十八、灰度发布
未来:
多服务器:
Load Balancer
/ \
API v1 API v2
先:
10%
用户进入新版。
确认:
错误率。
再:
100%。
二十九、灾难恢复方案
假设:
服务器损坏。
恢复:
新服务器
↓
安装Docker
↓
恢复.env
↓
恢复Vault
↓
恢复数据库
↓
启动服务
↓
验证License
三十、上线Checklist
域名
✅ DNS配置
✅ HTTPS
Server
✅ SSH安全
✅ Firewall
✅ Fail2ban
Docker
✅ 自动启动
✅ 日志限制
Database
✅ 密码
✅ 备份
✅ 索引
License
✅ 签名验证
✅ 私钥保护
✅ 激活限制
Payment
✅ Webhook测试
✅ 自动发货测试
Monitoring
✅ Grafana
✅ Alert
三十一、本阶段完成
| 模块 | 状态 |
|---|---|
| 服务器架构 | ✅ |
| Docker生产部署 | ✅ |
| PostgreSQL部署 | ✅ |
| Redis部署 | ✅ |
| Nginx | ✅ |
| HTTPS | ✅ |
| Cloudflare | ✅ |
| CI/CD | ✅ |
| 监控 | ✅ |
| 备份 | ✅ |
| 灾难恢复 | ✅ |
当前系统状态
现在已经达到:
Website
|
Payment
|
License Platform
|
Client SDK
|
User Software
已经具备:
- 商业销售能力
- 自动交付能力
- 安全授权能力
- 运维能力
下一部分:
第九部分(九):客户端商业发行、防破解与最终交付方案
将重点解决:
- Windows/macOS/Linux软件发行
- 安装包制作
- License SDK集成
- .NET应用保护
- 防逆向策略
- 防Patch方案
- 自动更新系统
- 崩溃收集
- 用户反馈系统
- 商业版本管理
这一部分完成后,你的软件就具备完整商业产品发行能力。
LicenseServer 商业授权系统开发实战文档 v1.0
第九部分(九):客户端商业发行、防破解与最终交付方案
本阶段完成:
从“开发完成的软件” → “可以销售给真实用户的商业软件”。
重点:
- Windows/macOS/Linux 多平台发行
- 安装包制作
- 自动更新
- License SDK集成
- 软件防篡改
- 逆向分析防护
- 崩溃收集
- 用户反馈
- 版本管理
- 商业交付流程
一、商业软件最终架构
最终产品结构:
官网
|
下载中心
|
+----------+----------+
| |
Windows版本 macOS版本
| |
Installer DMG / PKG
| |
+----------+----------+
|
Client App
|
License SDK
|
License Server
|
Activation
二、客户端技术路线建议
你的授权系统使用:
C# + .NET。
客户端推荐:
Windows
推荐:
Avalonia UI
原因:
- .NET原生
- 跨平台
- 商业支持好
结构:
.NET 10
+
Avalonia UI
+
License SDK
macOS
推荐:
Avalonia:
生成:
.app
Linux
支持:
AppImage
.deb
.rpm
三、客户端项目结构
例如:
软件:
MyEditor
结构:
MyEditor/
├── MyEditor.App
│
├── MyEditor.Core
├── MyEditor.License
├── MyEditor.Update
├── MyEditor.Logging
└── MyEditor.Installer
四、License SDK集成
客户端:
引用:
<ItemGroup>
<ProjectReference
Include="../LicenseSDK"
/>
</ItemGroup>
启动流程:
Application Start
|
|
Load License
|
|
Verify Signature
|
|
Check Expire
|
|
Check Feature
|
|
Start Software
五、商业版功能控制
不要:
创建多个软件。
例如:
错误:
MyEditor Free.exe
MyEditor Pro.exe
MyEditor Enterprise.exe
容易维护困难。
正确:
一个程序:
MyEditor.exe
通过License控制:
{
features:
[
"export",
"cloud",
"ai"
]
}
例如:
代码:
if(
license.HasFeature("AI")
)
{
EnableAI();
}
六、Feature授权系统
服务器:
定义:
Feature
id
name
description
例如:
| Feature | 版本 |
|---|---|
| Export PDF | Pro |
| AI Assistant | Enterprise |
| Cloud Sync | Enterprise |
客户端:
缓存:
FeatureFlags
七、Windows安装包
推荐:
WiX Toolset
微软工业标准。
支持:
- MSI
- 企业部署
- Group Policy
安装:
MyEditorSetup.msi
目录:
Program Files
|
MyEditor
八、Windows安装流程
用户:
双击:
Setup.exe
流程:
检测.NET Runtime
|
安装程序
|
创建快捷方式
|
首次启动
|
输入License
九、macOS发行
格式:
DMG
例如:
MyEditor.dmg
用户:
拖动:
MyEditor.app
↓
Applications
十、macOS签名
苹果要求:
必须:
Code Signing。
使用:
codesign
示例:
codesign \
--deep \
--force \
--sign \
"My Developer ID" \
MyEditor.app
十一、公证(Notarization)
上传:
Apple:
notarytool
流程:
Build
↓
Sign
↓
Upload
↓
Apple Scan
↓
Staple Ticket
↓
Release
十二、Linux发行
支持:
AppImage
优点:
一个文件。
例如:
MyEditor.AppImage
Debian
生成:
myeditor.deb
安装:
sudo dpkg -i myeditor.deb
十三、自动更新系统
商业软件必须有。
不要:
让用户重新下载。
架构:
Client
|
Check Update
|
Update Server
|
Download Package
|
Verify Signature
|
Install
十四、Update Server设计
数据库:
增加:
SoftwareVersion
Id
Product
Version
Platform
DownloadUrl
Hash
Signature
ReleaseDate
例如:
{
version:"2.1.0",
platform:"windows",
url:
"https://download.xxx.com/app.exe",
sha256:
"xxxx"
}
十五、更新安全
不能:
直接下载执行。
必须:
验证:
Hash
例如:
SHA256:
download.exe
↓
SHA256
↓
compare
Signature
发布:
私钥签名。
客户端:
公钥验证。
十六、更新流程
查询版本
↓
发现2.0 -> 2.1
↓
下载
↓
验证签名
↓
备份旧版本
↓
替换
↓
启动新版
十七、崩溃收集系统
商业软件必须知道:
用户哪里崩。
推荐:
Sentry
或者:
自建:
Crash Server
收集:
- Exception
- OS
- 软件版本
- License版本
- Hardware
不要收集:
- 用户文件
- 密码
- License Key
十八、日志系统
客户端:
分级:
Trace
Debug
Info
Warning
Error
Critical
保存:
Windows:
%LOCALAPPDATA%/MyEditor/logs
macOS:
~/Library/Logs/MyEditor
Linux:
~/.local/share/MyEditor/logs
十九、防破解原则
首先明确:
商业软件不存在绝对防破解。
目标:
提高破解成本。
二十、不要把核心秘密放客户端
错误:
if(key=="ABC123")
unlock
破解者:
直接找到。
正确:
客户端:
只保存:
Public Key
服务器:
保存:
Private Key
二十一、代码混淆
.NET:
推荐:
商业
- Eazfuscator.NET
- Babel Obfuscator
免费
- ConfuserEx
保护:
- 类名
- 方法名
- 字符串
- 控制流
二十二、字符串保护
不要:
var url=
"https://api.xxx.com";
改:
运行时解密。
例如:
Encrypted String
↓
Decrypt
↓
Use
二十三、防Patch设计
不要:
只有:
bool IsLicensed
改成:
多个验证点:
LicenseValidator
FeatureManager
RuntimeCheck
OnlineCheck
攻击者需要修改多个位置。
二十四、完整性检查
检测:
程序文件修改。
例如:
启动:
Calculate SHA256
↓
Compare
↓
Continue
服务器:
保存:
Official Hash
二十五、反调试
检测:
- Debugger
- 调试环境
- 修改时间
例如:
.NET:
Debugger.IsAttached
注意:
只能作为辅助。
二十六、虚拟机检测
商业软件可以:
检测异常环境。
例如:
- 大量虚拟设备
- 异常硬件
但:
不要误伤正常用户。
二十七、License设备管理
用户后台:
显示:
My Devices
Windows PC
MacBook
Linux Server
操作:
Remove Device
二十八、设备解绑策略
例如:
Personal:
每30天:
允许解绑3次。
Professional:
无限。
二十九、用户账户系统
商业软件建议:
不要只靠Key。
采用:
Account
+
License
+
Device
登录:
Email
Password
MFA
三十、最终交付包
最终发布:
Release/
Windows/
MyEditorSetup.exe
macOS/
MyEditor.dmg
Linux/
MyEditor.AppImage
Documentation/
UserGuide.pdf
License/
EULA.txt
三十一、软件版本管理
采用:
Semantic Version:
格式:
Major.Minor.Patch
2.5.1
规则:
重大变化:
3.0.0
新增功能:
2.6.0
Bug:
2.5.2
三十二、商业发布流程
完整流程:
开发
↓
自动测试
↓
Build
↓
签名
↓
生成安装包
↓
上传服务器
↓
创建Release
↓
更新官网
↓
通知用户
三十三、最终安全链路
完整保护:
支付
|
License生成
|
ECDSA签名
|
客户端验证
|
本地加密缓存
|
Feature控制
|
运行检查
|
自动更新验证
三十四、本阶段完成
| 模块 | 状态 |
|---|---|
| Windows发行 | ✅ |
| macOS发行 | ✅ |
| Linux发行 | ✅ |
| 安装包设计 | ✅ |
| 自动更新 | ✅ |
| 签名验证 | ✅ |
| 崩溃收集 | ✅ |
| 日志系统 | ✅ |
| 防破解策略 | ✅ |
| 版本管理 | ✅ |
| 商业交付流程 | ✅ |
当前项目完成度
现在已经达到:
商业软件生态
官网
|
支付系统
|
License Server
|
Admin Console
|
Client SDK
|
用户软件
已经具备:
✅ 商业销售 ✅ 自动交付 ✅ 在线授权 ✅ 离线授权 ✅ 多设备控制 ✅ 自动升级 ✅ 运营管理
下一部分:
第九部分(十):完整源码工程结构 + Docker部署文件 + CI/CD + 商业上线Checklist
这是整个项目文档最后整合阶段,将包含:
- 完整Git仓库结构
- Backend源码布局
- Client SDK源码布局
- Admin源码布局
- Docker Compose完整文件
- Kubernetes扩展方案
- GitHub Actions完整CI/CD
- 环境变量模板
- 生产上线检查表
- 后期维护路线图
完成后,你会得到一套完整的:
C#/.NET 商业License SaaS平台工程蓝图。
目前按照我们规划的《LicenseServer 商业授权系统开发实战文档》来看:
第九部分(十)是整个“大章节”的最后一部分。
也就是说:
完成第九部分(十)后,核心设计文档阶段基本完成。
不过,如果目标是达到真正可商业运行的软件授权平台,后面我建议再增加几个“工程落地阶段”,因为前面的内容偏架构设计和核心实现,还需要把它变成一个真正可维护的项目。
完整路线如下:
当前已完成
第九部分(一)~(十)
| 章节 | 内容 | 状态 |
|---|---|---|
| 九(一) | License Engine核心实现 | ✅ |
| 九(二) | 数据库与后端核心代码 | ✅ |
| 九(三) | License核心业务实现 | ✅ |
| 九(四) | 客户端SDK完整实现 | ✅ |
| 九(五) | 生产级安全加固 | ✅ |
| 九(六) | 支付系统+自动发货 | ✅ |
| 九(七) | Admin Console后台 | ✅ |
| 九(八) | 生产部署上线方案 | ✅ |
| 九(九) | 客户端商业发行、防破解 | ✅ |
| 九(十) | 源码结构+Docker+CI/CD+上线Checklist | 待完成 |
第九部分(十)完成后
还建议继续:
第十部分:完整项目工程化实现
(推荐继续)
目标:
把架构变成真正代码仓库。
包含:
第十(一):完整Monorepo工程设计
内容:
LicensePlatform/
├── src/
│
├── License.Api
│
├── License.Application
│
├── License.Domain
│
├── License.Infrastructure
├── License.Worker
├── License.Admin
├── License.Client.SDK
├── License.Client.Demo
├── Tests/
├── Deploy/
└── Docs/
包括:
- Clean Architecture
- Domain Driven Design
- CQRS
- Repository模式
- Dependency Injection
第十(二):ASP.NET Core完整Backend实现
包括:
实际代码:
- Program.cs
- Middleware
- JWT认证
- RBAC
- EF Core
- PostgreSQL
- Redis
- Background Worker
- FluentValidation
- Exception Handler
第十(三):数据库完整实现
包括:
完整:
Users
Roles
Permissions
Products
Plans
Orders
Payments
Licenses
Devices
AuditLogs
Subscriptions
Coupons
提供:
- Entity
- DbContext
- Migration
- Seed数据
第十(四):License Server源码实现
完整:
包括:
- Key生成
- Hash
- ECDSA签名
- 激活流程
- 设备绑定
- 解绑
- Heartbeat
- 离线License
第十(五):Client SDK生产代码
包括:
Windows/macOS/Linux:
- LicenseManager
- Storage
- Crypto
- DeviceFingerprint
- UpdateManager
第十(六):Admin Console完整代码
包括:
Next.js:
页面:
Dashboard
Users
Licenses
Orders
Payments
Audit
Settings
组件:
- Table
- Form
- Chart
- Permission
第十(七):自动更新系统实现
包括:
Server:
Update API
Client:
Update Agent
功能:
- 检查版本
- 下载
- SHA256验证
- 签名验证
- 自动安装
第十(八):测试体系
商业软件必须:
包括:
单元测试
xUnit
API测试
WebApplicationFactory
集成测试
Docker Test Containers
安全测试
OWASP
第十(九):商业运维体系
包括:
生产运营:
- 用户支持
- License查询
- 数据分析
- 错误追踪
- 安全响应
第十(十):最终商业版本发布
包含:
最终:
LicensePlatform v1.0
├── Server
├── Admin
├── SDK
├── Client Demo
├── Installer
├── Documentation
└── Deployment
后续高级扩展(可选)
如果以后规模扩大,还可以继续:
第十一部分:企业级架构升级
适合:
百万用户。
内容:
- Kubernetes
- 微服务拆分
- 消息队列
- Kafka
- RabbitMQ
- 多区域部署
- 高可用数据库
- 灾备
第十二部分:商业SaaS运营体系
内容:
- 用户增长
- 产品分析
- AB测试
- 邮件营销
- CRM
- 客服系统
第十三部分:高级安全攻防
内容:
- 红队测试
- 逆向分析
- Anti-Tamper
- HSM
- TPM
- Secure Boot
- 代码签名体系
第十四部分:开源级完整项目模板
最终整理成:
GitHub/GitLab项目:
包括:
- README
- Architecture Diagram
- Docker Compose
- CI/CD
- Development Guide
- Production Guide
总结
如果按照完整商业级路线:
| 阶段 | 状态 |
|---|---|
| 第九部分(一)-(九) | 已完成 |
| 第九部分(十) | 下一步 |
| 第十部分 | 完整源码实现阶段 |
| 第十一部分 | 企业级扩展 |
| 第十二部分 | 商业运营 |
| 第十三部分 | 高级安全 |
所以回答你的问题:
核心系统设计还剩 1 部分(第九部分十)。
但是如果目标是:
“我要真的开发出来并销售的软件授权平台”
建议继续完成:
第十部分(完整源码工程实现)
因为前面的内容是“架构蓝图”,第十部分才会进入真正的:
- 文件结构
- 类设计
- 数据库代码
- API代码
- Docker文件
- CI/CD脚本
- 可运行项目
下一步我建议直接开始:
第九部分(十):完整源码工程结构 + Docker部署文件 + CI/CD + 商业上线Checklist
完成后再进入:
第十部分:完整源码实现阶段
这样整个项目会从设计 → 工程 → 商业产品完整闭环。
LicenseServer 商业授权系统开发实战文档 v1.0
第九部分(十):完整源码工程结构 + Docker部署文件 + CI/CD + 商业上线 Checklist
本部分是第九章最后阶段。
目标:
把前面设计的:
- License Server
- Client SDK
- Admin Console
- Payment System
- Deployment System
整理成一个真正的软件工程项目。
最终目标:
一个可以 Git 管理、多人开发、自动部署、商业上线的完整项目。
一、最终项目仓库结构(Monorepo)
推荐使用:
GitHub / GitLab Monorepo。
项目名称:
LicensePlatform
完整结构:
LicensePlatform/
│
├── README.md
├── LICENSE
├── .gitignore
├── docker-compose.yml
├── .env.example
│
├── src/
│
├── Server/
│ │
│ ├── License.Api
│ │
│ ├── License.Application
│ │
│ ├── License.Domain
│ │
│ ├── License.Infrastructure
│ │
│ ├── License.Worker
│ │
│ └── License.Migrations
│
├── Admin/
│
│ └── license-admin
│
├── SDK/
│
│ └── License.Client.SDK
│
├── Demo/
│
│ └── Demo.Desktop.App
├── Tests/
│
├── License.Api.Tests
│
├── License.Domain.Tests
│
└── License.Integration.Tests
├── Deploy/
│
├── docker/
│
├── nginx/
│
├── postgres/
│
├── redis/
│
└── vault/
├── CI/
│
└── github-actions
└── Docs/
├── Architecture.md
├── API.md
├── Deployment.md
└── Security.md
二、后端源码结构
采用:
Clean Architecture
结构:
License.Api
|
|
License.Application
|
|
License.Domain
|
|
License.Infrastructure
三、License.Domain
负责:
核心业务。
包含:
Domain/
Entities/
License.cs
User.cs
Device.cs
Order.cs
ValueObjects/
LicenseKey.cs
DeviceFingerprint.cs
Enums/
LicenseStatus.cs
Events/
LicenseActivatedEvent.cs
四、License.Application
业务逻辑。
例如:
Application/
Services/
LicenseService.cs
ActivationService.cs
PaymentService.cs
Interfaces/
ILicenseRepository.cs
IPaymentProvider.cs
Commands/
ActivateLicenseCommand.cs
Queries/
GetLicenseQuery.cs
五、License.Infrastructure
负责:
外部资源。
包括:
Infrastructure/
Persistence/
LicenseDbContext.cs
Repositories/
LicenseRepository.cs
Crypto/
EcdsaService.cs
Email/
MailService.cs
Payments/
StripeProvider.cs
Storage/
VaultService.cs
六、License.Api
HTTP接口。
结构:
License.Api/
Controllers/
LicenseController.cs
PaymentController.cs
AdminController.cs
Middleware/
ExceptionMiddleware.cs
Authentication/
JwtHandler.cs
Program.cs
七、核心API列表
用户接口
注册
POST
/api/auth/register
登录
POST
/api/auth/login
License接口
激活
POST
/api/license/activate
请求:
{
"licenseKey":
"48D0-61CD...",
"fingerprint":
"xxxx"
}
返回:
{
"success":
true,
"expireAt":
"2027-01-01"
}
心跳
POST
/api/license/heartbeat
查询设备
GET
/api/license/devices
八、数据库迁移目录
使用:
EF Core。
结构:
License.Migrations/
Migrations/
20260811_Init.cs
20260812_AddPayment.cs
执行:
开发:
dotnet ef migrations add Init
生产:
dotnet ef database update
九、Admin Console结构
Next.js:
license-admin/
app/
(auth)/login
(admin)/dashboard
(admin)/users
(admin)/licenses
(admin)/orders
components/
ui/
tables/
charts/
lib/
api.ts
hooks/
types/
十、Client SDK结构
License.Client.SDK/
LicenseManager.cs
LicenseClient.cs
LicenseValidator.cs
DeviceFingerprint.cs
LicenseStorage.cs
Crypto/
EcdsaVerifier.cs
AesEncryptor.cs
Models/
LicenseInfo.cs
ActivationResult.cs
十一、环境变量模板
根目录:
.env.example
内容:
# Database
POSTGRES_DB=license
POSTGRES_USER=license_api
POSTGRES_PASSWORD=password
# JWT
JWT_SECRET=change_me
# Redis
REDIS_PASSWORD=password
# Stripe
STRIPE_SECRET_KEY=
STRIPE_WEBHOOK_SECRET=
# SMTP
SMTP_HOST=
SMTP_USER=
SMTP_PASSWORD=
# Vault
VAULT_ADDR=
VAULT_TOKEN=
# Domain
API_DOMAIN=
ADMIN_DOMAIN=
十二、Docker生产部署结构
最终:
Docker
├── nginx
├── api
├── admin
├── worker
├── postgres
├── redis
└── vault
十三、生产docker-compose.yml
示例:
services:
postgres:
image: postgres:17
restart: always
environment:
POSTGRES_DB:
${POSTGRES_DB}
POSTGRES_USER:
${POSTGRES_USER}
POSTGRES_PASSWORD:
${POSTGRES_PASSWORD}
volumes:
- postgres_data:/var/lib/postgresql/data
redis:
image:
redis:7-alpine
restart:
always
api:
image:
company/license-api:${VERSION}
restart:
always
depends_on:
- postgres
- redis
worker:
image:
company/license-worker:${VERSION}
admin:
image:
company/license-admin:${VERSION}
nginx:
image:
nginx:latest
ports:
- "80:80"
- "443:443"
volumes:
postgres_data:
十四、Docker镜像设计
API Dockerfile
FROM mcr.microsoft.com/dotnet/aspnet:10.0
WORKDIR /app
COPY publish .
ENTRYPOINT [
"dotnet",
"License.Api.dll"
]
Admin Dockerfile
FROM node:24-alpine
WORKDIR /app
COPY . .
RUN npm install
RUN npm run build
CMD [
"npm",
"start"
]
十五、GitHub Actions CI/CD
目录:
.github/workflows/
build.yml
deploy.yml
十六、自动测试流程
提交代码:
Git Push
↓
GitHub Actions
↓
Restore
↓
Build
↓
Test
↓
Docker Build
↓
Security Scan
↓
Deploy
十七、Backend CI示例
name:
backend-build
on:
push:
branches:
- main
jobs:
build:
runs-on:
ubuntu-latest
steps:
- uses:
actions/checkout@v4
- name:
dotnet test
run:
dotnet test
- name:
docker build
run:
docker build .
十八、安全扫描流程
加入:
Trivy
扫描:
Docker镜像。
trivy image license-api
Dependabot
检查:
- NuGet漏洞
- npm漏洞
十九、发布流程
正式版本:
Developer
↓
Pull Request
↓
Review
↓
Merge
↓
CI Build
↓
Create Version
↓
Docker Push
↓
Production Deploy
二十、版本管理
采用:
Semantic Version。
例如:
v1.0.0
规则:
Major
重大变化:
2.0.0
Minor
新增功能:
1.5.0
Patch
Bug修复:
1.5.1
二十一、生产上线 Checklist
域名
✅ DNS配置
✅ SSL证书
✅ Cloudflare开启
服务器
✅ Ubuntu/Debian更新
✅ SSH安全
✅ 防火墙
✅ Fail2Ban
Docker
✅ 自动启动
✅ 日志限制
✅ 镜像版本固定
数据库
✅ 强密码
✅ 自动备份
✅ 恢复测试
License
✅ ECDSA签名
✅ 私钥Vault保存
✅ License Hash保存
支付
✅ Stripe测试
✅ Webhook验证
✅ 自动发货测试
客户端
✅ Windows安装测试
✅ macOS签名
✅ Linux AppImage测试
安全
✅ OWASP扫描
✅ API限流
✅ 管理员MFA
二十二、上线前测试矩阵
Windows
测试:
- Windows 10
- Windows 11
macOS
测试:
- Intel Mac
- Apple Silicon
Linux
测试:
- Ubuntu
- Debian
- Fedora
二十三、灾备检查
模拟:
服务器删除。
验证:
重新部署
↓
恢复数据库
↓
恢复Vault
↓
恢复License
↓
用户继续激活
二十四、最终商业版本结构
最终交付:
LicensePlatform v1.0
├── Backend
├── Admin Console
├── Client SDK
├── Demo Application
├── Docker Deployment
├── CI/CD
├── Documentation
└── Security Policy
二十五、第九部分全部完成
至此:
第九部分(一)~(十)全部完成
整个商业License系统已经包含:
| 模块 | 完成 |
|---|---|
| 授权引擎 | ✅ |
| 客户端SDK | ✅ |
| 支付系统 | ✅ |
| 自动发货 | ✅ |
| 后台管理 | ✅ |
| 安全体系 | ✅ |
| 生产部署 | ✅ |
| CI/CD | ✅ |
| 商业发行 | ✅ |
下一阶段:第十部分(完整源码工程实现阶段)
这一部分会从“设计文档”进入真正开发:
将开始写:
第十(一):Monorepo完整初始化 + .NET Solution + Next.js工程创建
内容:
- Git初始化
- Solution创建
- 项目引用关系
- NuGet配置
- Docker开发环境
- PostgreSQL初始化
- EF Core配置
- 第一批真实代码
也就是说:
前面约 80% 是架构设计。
第十部分开始:
真正写代码,逐文件实现。
完成第十部分后,你将拥有一个可以直接开发的商业License平台源码。
LicenseServer 商业授权系统开发实战文档 v1.0
第十部分:完整源码工程实现阶段
第十(一):Monorepo 初始化 + .NET Solution + Next.js 工程创建
从本部分开始,我们进入真实工程代码阶段。
目标:
创建一个可以长期维护的商业级代码仓库:
- Backend(ASP.NET Core)
- Admin Console(Next.js)
- Client SDK(.NET)
- Worker Service
- Database
- Docker开发环境
- CI/CD基础
一、开发环境统一规范
由于你需要:
- macOS开发
- Windows开发
- Linux开发
所以必须统一工具链。
推荐:
| 组件 | 版本 |
|---|---|
| .NET | 10 LTS |
| C# | 14 |
| Node.js | 24 LTS |
| Bun | 1.2+ |
| TypeScript | 5.9+(TS 7正式稳定后再升级) |
| PostgreSQL | 17 |
| Redis | 7 |
| Docker | 最新稳定版 |
| Git | 2.45+ |
二、为什么不建议现在直接使用 TypeScript 7.0?
你之前提到想使用:
TypeScript 7.0
建议:
生产项目不要第一时间追最新大版本。
原因:
TypeScript升级影响:
- Next.js
- ESLint
- Vite
- Turbopack
- 类型声明
商业项目建议:
当前:
TypeScript 5.9.x
等:
Next.js
React
VSCode
主要生态
全部验证后:
再升级:
TypeScript 7.x
三、为什么选择 Bun?
你的问题:
Node.js能不能用Bun替代?
答案:
可以。
但是建议:
开发环境
使用:
Bun
生产环境
Admin Console:
推荐:
Node.js 24 LTS
原因:
商业稳定优先。
推荐组合:
开发:
Bun
↓
Next.js
↓
TypeScript
生产:
Node.js
↓
Next.js Standalone
↓
Docker
四、创建项目根目录
例如:
Linux/macOS:
mkdir LicensePlatform
cd LicensePlatform
git init
Windows:
PowerShell:
mkdir LicensePlatform
cd LicensePlatform
git init
五、初始化目录
创建:
mkdir src
mkdir deploy
mkdir docs
mkdir tests
最终:
LicensePlatform
├── src
├── deploy
├── docs
├── tests
└── README.md
六、创建.NET Solution
进入:
cd src
创建:
dotnet new sln \
-n LicensePlatform
生成:
LicensePlatform.sln
七、创建Backend项目
进入:
mkdir Server
cd Server
1. API项目
dotnet new webapi \
-n License.Api
2. Domain项目
dotnet new classlib \
-n License.Domain
3. Application项目
dotnet new classlib \
-n License.Application
4. Infrastructure项目
dotnet new classlib \
-n License.Infrastructure
5. Worker项目
dotnet new worker \
-n License.Worker
八、加入Solution
返回:
cd ..
执行:
dotnet sln add \
Server/**/*.csproj
查看:
dotnet sln list
应该:
License.Api
License.Domain
License.Application
License.Infrastructure
License.Worker
九、项目引用关系设计
Clean Architecture:
API
|
|
Application
|
|
Domain
Infrastructure
|
|
Database
规则:
Domain
不能引用任何东西。
Application
引用:
Domain
Infrastructure
引用:
Application
Domain
API
引用:
Application
Infrastructure
执行:
Application:
dotnet add \
License.Application.csproj \
reference \
../License.Domain/License.Domain.csproj
Infrastructure:
dotnet add \
License.Infrastructure.csproj \
reference \
../License.Application/License.Application.csproj
API:
dotnet add \
License.Api.csproj \
reference \
../License.Application/License.Application.csproj
dotnet add \
License.Api.csproj \
reference \
../License.Infrastructure/License.Infrastructure.csproj
十、创建Client SDK项目
返回:
cd ../../
创建:
mkdir SDK
cd SDK
执行:
dotnet new classlib \
-n License.Client.SDK
加入:
cd ../..
dotnet sln src/LicensePlatform.sln add \
src/SDK/License.Client.SDK/License.Client.SDK.csproj
十一、创建Demo客户端
用于测试授权。
mkdir Demo
cd Demo
dotnet new console \
-n License.Demo.Client
加入:
dotnet sln add \
Demo/License.Demo.Client/*.csproj
十二、创建Admin Console
返回根目录:
cd LicensePlatform
mkdir src/Admin
cd src/Admin
使用Bun:
bun create next-app license-admin
选择:
✔ TypeScript
✔ ESLint
✔ Tailwind
✔ App Router
✔ src directory
✔ Turbopack
进入:
cd license-admin
测试:
bun dev
访问:
http://localhost:3000
十三、Admin依赖安装
安装:
bun add axios
bun add \
@tanstack/react-query
bun add \
zod
bun add \
react-hook-form
UI:
bunx shadcn@latest init
安装组件:
bunx shadcn@latest add \
button \
card \
table \
dialog \
form
十四、最终源码结构
现在:
LicensePlatform/
src/
├── Server/
│
├── License.Api
│
├── License.Domain
│
├── License.Application
│
├── License.Infrastructure
│
└── License.Worker
├── SDK/
│
└── License.Client.SDK
├── Demo/
│
└── License.Demo.Client
└── Admin/
└── license-admin
deploy/
docs/
tests/
十五、添加.gitignore
根目录:
dotnet new gitignore
追加:
node_modules/
.next/
.env
.env.*
bin/
obj/
.idea/
.vscode/
十六、添加Directory.Build.props
根目录:
创建:
Directory.Build.props
内容:
<Project>
<PropertyGroup>
<Nullable>
enable
</Nullable>
<ImplicitUsings>
enable
</ImplicitUsings>
<TreatWarningsAsErrors>
false
</TreatWarningsAsErrors>
<LangVersion>
latest
</LangVersion>
</PropertyGroup>
</Project>
十七、统一NuGet版本管理
创建:
Directory.Packages.props
例如:
<Project>
<PropertyGroup>
<ManagePackageVersionsCentrally>
true
</ManagePackageVersionsCentrally>
</PropertyGroup>
<ItemGroup>
<PackageVersion
Include="Microsoft.EntityFrameworkCore"
Version="10.0.0"
/>
</ItemGroup>
</Project>
十八、第一次完整编译
根目录:
dotnet build
Next:
cd src/Admin/license-admin
bun run build
十九、本阶段完成
完成内容:
| 项目 | 状态 |
|---|---|
| Monorepo | ✅ |
| .NET Solution | ✅ |
| Clean Architecture结构 | ✅ |
| API项目 | ✅ |
| Domain项目 | ✅ |
| Infrastructure项目 | ✅ |
| Worker项目 | ✅ |
| Client SDK项目 | ✅ |
| Next.js Admin项目 | ✅ |
| Bun开发环境 | ✅ |
| 跨平台基础 | ✅ |
下一章节
第十(二):ASP.NET Core Backend 核心代码实现
将开始真正写:
- Program.cs
- Dependency Injection
- EF Core
- PostgreSQL连接
- Redis配置
- JWT认证
- 用户系统
- License API
- Middleware
- 全局异常处理
也就是开始进入:
第一行商业系统后端代码实现阶段。
LicenseServer 商业授权系统开发实战文档 v1.0
第十部分:完整源码工程实现阶段
第十(二):ASP.NET Core Backend 核心代码实现
本阶段开始进入真正 Backend 编码阶段。
目标:
完成:
- ASP.NET Core API基础架构
- Clean Architecture启动
- Dependency Injection
- PostgreSQL数据库连接
- Entity Framework Core
- Redis缓存
- JWT认证
- 全局异常处理
- API版本控制
- Swagger
- 健康检查
最终:
Client
|
HTTPS
|
License.Api
|
Application Layer
|
Infrastructure
|
PostgreSQL + Redis
一、Backend最终结构
完善:
License.Api
├── Controllers
│
├── AuthController.cs
├── LicenseController.cs
├── HealthController.cs
├── Middleware
│
└── ExceptionMiddleware.cs
├── Extensions
│
├── ServiceExtensions.cs
├── SwaggerExtensions.cs
├── Configuration
│
├── JwtSettings.cs
└── Program.cs
License.Application
├── Interfaces
├── Services
├── DTOs
├── Validators
License.Infrastructure
├── Persistence
├── Repositories
├── Cache
├── Security
二、安装Backend依赖
进入:
cd src/Server
License.Api
dotnet add License.Api package \
Microsoft.AspNetCore.Authentication.JwtBearer
Swagger:
dotnet add License.Api package \
Swashbuckle.AspNetCore
Infrastructure
EF Core:
dotnet add License.Infrastructure package \
Microsoft.EntityFrameworkCore
dotnet add License.Infrastructure package \
Npgsql.EntityFrameworkCore.PostgreSQL
Redis:
dotnet add License.Infrastructure package \
StackExchange.Redis
Application
验证:
dotnet add License.Application package \
FluentValidation
三、配置文件设计
appsettings.json
路径:
License.Api/appsettings.json
内容:
{
"ConnectionStrings": {
"DefaultConnection":
"Host=localhost;Port=5432;Database=license;Username=license;Password=password"
},
"Redis": {
"Connection":
"localhost:6379"
},
"Jwt": {
"Issuer":
"LicenseServer",
"Audience":
"LicenseClient",
"Secret":
"CHANGE_ME"
},
"Logging": {
"LogLevel": {
"Default":
"Information"
}
}
}
四、创建Domain实体
进入:
License.Domain
创建:
Entities/User.cs
代码:
namespace License.Domain.Entities;
public class User
{
public Guid Id { get; set; }
public string Email { get; set; } = "";
public string PasswordHash { get; set; } = "";
public DateTime CreatedAt { get; set; }
=
DateTime.UtcNow;
}
五、License实体
创建:
Entities/License.cs
代码:
namespace License.Domain.Entities;
public class License
{
public Guid Id { get; set; }
public string LicenseKeyHash { get; set; } = "";
public Guid UserId { get; set; }
public int MaxDevices { get; set; }
public DateTime ExpireAt { get; set; }
public bool Enabled { get; set; }
}
六、创建DbContext
路径:
License.Infrastructure/Persistence
创建:
LicenseDbContext.cs
代码:
using Microsoft.EntityFrameworkCore;
using License.Domain.Entities;
namespace License.Infrastructure.Persistence;
public class LicenseDbContext
:
DbContext
{
public LicenseDbContext(
DbContextOptions options)
:
base(options)
{
}
public DbSet<User> Users => Set<User>();
public DbSet<License> Licenses => Set<License>();
}
七、Infrastructure依赖注入
创建:
DependencyInjection.cs
代码:
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
namespace License.Infrastructure;
public static class DependencyInjection
{
public static IServiceCollection AddInfrastructure(
this IServiceCollection services,
IConfiguration config)
{
services.AddDbContext<LicenseDbContext>(
options =>
{
options.UseNpgsql(
config.GetConnectionString(
"DefaultConnection"));
});
return services;
}
}
八、Application层服务接口
创建:
Interfaces/ILicenseService.cs
代码:
using License.Domain.Entities;
namespace License.Application.Interfaces;
public interface ILicenseService
{
Task<License?>
GetLicenseAsync(
string key);
}
九、License Service实现
创建:
Services/LicenseService.cs
代码:
using License.Application.Interfaces;
using License.Infrastructure.Persistence;
using Microsoft.EntityFrameworkCore;
namespace License.Application.Services;
public class LicenseService
:
ILicenseService
{
private readonly LicenseDbContext _db;
public LicenseService(
LicenseDbContext db)
{
_db=db;
}
public async Task<Domain.Entities.License?>
GetLicenseAsync(
string key)
{
return await _db.Licenses
.FirstOrDefaultAsync(
x=>x.LicenseKeyHash==key);
}
}
十、Application依赖注入
创建:
DependencyInjection.cs
代码:
using Microsoft.Extensions.DependencyInjection;
using License.Application.Interfaces;
using License.Application.Services;
namespace License.Application;
public static class DependencyInjection
{
public static IServiceCollection AddApplication(
this IServiceCollection services)
{
services.AddScoped
<
ILicenseService,
LicenseService
>();
return services;
}
}
十一、Program.cs核心配置
修改:
License.Api/Program.cs
代码:
using License.Application;
using License.Infrastructure;
var builder =
WebApplication.CreateBuilder(args);
builder.Services
.AddApplication();
builder.Services
.AddInfrastructure(
builder.Configuration);
builder.Services
.AddControllers();
builder.Services
.AddEndpointsApiExplorer();
builder.Services
.AddSwaggerGen();
var app =
builder.Build();
if(app.Environment.IsDevelopment())
{
app.UseSwagger();
app.UseSwaggerUI();
}
app.UseHttpsRedirection();
app.MapControllers();
app.Run();
十二、创建健康检查
安装:
无需额外包。
Program加入:
builder.Services
.AddHealthChecks();
下面:
app.MapHealthChecks(
"/health");
测试:
浏览:
https://localhost:7000/health
返回:
Healthy
十三、创建License Controller
路径:
Controllers/LicenseController.cs
代码:
using Microsoft.AspNetCore.Mvc;
using License.Application.Interfaces;
namespace License.Api.Controllers;
[ApiController]
[Route("api/license")]
public class LicenseController
:
ControllerBase
{
private readonly ILicenseService _service;
public LicenseController(
ILicenseService service)
{
_service=service;
}
[HttpGet("{key}")]
public async Task<IActionResult>
Get(
string key)
{
var license =
await _service
.GetLicenseAsync(key);
if(license==null)
return NotFound();
return Ok(license);
}
}
十四、运行测试
启动:
dotnet run \
--project License.Api
访问:
GET
/api/license/test
返回:
{
"id":"xxx",
"enabled":true
}
十五、添加JWT认证框架
安装:
已经:
Microsoft.AspNetCore.Authentication.JwtBearer
Program增加:
builder.Services
.AddAuthentication()
.AddJwtBearer();
增加:
app.UseAuthentication();
app.UseAuthorization();
十六、添加全局异常处理中间件
创建:
Middleware/ExceptionMiddleware.cs
代码:
public class ExceptionMiddleware
{
private readonly RequestDelegate _next;
public ExceptionMiddleware(
RequestDelegate next)
{
_next=next;
}
public async Task Invoke(
HttpContext context)
{
try
{
await _next(context);
}
catch(Exception ex)
{
context.Response.StatusCode=500;
await context.Response.WriteAsync(
"Internal Server Error");
}
}
}
注册:
Program:
app.UseMiddleware
<ExceptionMiddleware>();
十七、本阶段完成
完成:
| 模块 | 状态 |
|---|---|
| ASP.NET Core基础 | ✅ |
| Clean Architecture | ✅ |
| EF Core | ✅ |
| PostgreSQL连接 | ✅ |
| Redis准备 | ✅ |
| DI系统 | ✅ |
| License API | ✅ |
| Health Check | ✅ |
| Swagger | ✅ |
| 异常处理中间件 | ✅ |
下一章节
第十(三):数据库完整实现 + EF Core Migration + Repository模式
下一步将实现:
- 完整数据库模型
包括:
Users
Roles
Permissions
Products
Plans
Orders
Payments
Licenses
Devices
AuditLogs
Subscriptions
Coupons
- Entity关系设计
- EF Core Fluent API配置
- Migration生成
- Seed初始化管理员
- Repository + UnitOfWork
完成后,Backend将从“框架”进入真正商业业务数据库层。
LicenseServer 商业授权系统开发实战文档 v1.0
第十部分:完整源码工程实现阶段
第十(三):数据库完整实现 + EF Core Migration + Repository模式
本阶段进入:
商业授权平台核心数据层实现。
目标:
完成:
- PostgreSQL数据库设计
- Entity Framework Core实体模型
- Fluent API关系配置
- 数据库Migration
- 初始化数据Seed
- Repository模式
- Unit Of Work
- 数据访问规范
最终数据库架构:
Users
|
+----------+-----------+
| |
Licenses Orders
|
Devices
Products
|
Plans
Payments
AuditLogs
Roles
|
Permissions
一、数据库技术选型
采用:
| 组件 | 版本 |
|---|---|
| 数据库 | PostgreSQL 17 |
| ORM | EF Core 10 |
| 驱动 | Npgsql |
| 迁移 | EF Core Migration |
| 缓存 | Redis 7 |
原因:
PostgreSQL适合:
- SaaS系统
- 高并发查询
- JSON数据
- 索引能力
- 数据分析
二、完整数据库模型
最终表:
Users
|
├── Roles
|
├── Licenses
|
├── Orders
|
├── Payments
|
├── Devices
|
├── AuditLogs
Products
|
Plans
Subscriptions
Coupons
Permissions
三、基础实体设计
目录:
License.Domain
└── Entities
1. User实体
文件:
Entities/User.cs
namespace License.Domain.Entities;
public class User
{
public Guid Id {get;set;}
public string Email {get;set;} = "";
public string PasswordHash {get;set;} = "";
public bool EmailVerified {get;set;}
public DateTime CreatedAt {get;set;}
= DateTime.UtcNow;
public Guid? RoleId {get;set;}
public Role? Role {get;set;}
public ICollection<License>
Licenses {get;set;}
= new List<License>();
}
四、Role权限模型
Role
public class Role
{
public Guid Id {get;set;}
public string Name {get;set;}=""
public ICollection<User>
Users {get;set;}
=new List<User>();
public ICollection<RolePermission>
Permissions {get;set;}
=new List<RolePermission>();
}
Permission
public class Permission
{
public Guid Id {get;set;}
public string Code {get;set;}=""
public string Description {get;set;}=""
}
RolePermission
多对多关系:
public class RolePermission
{
public Guid RoleId {get;set;}
public Role Role {get;set;}=null!;
public Guid PermissionId {get;set;}
public Permission Permission {get;set;}=null!;
}
五、Product产品模型
对应:
软件产品。
例如:
PhotoManager Pro
VideoTool Enterprise
实体:
public class Product
{
public Guid Id {get;set;}
public string Name {get;set;}="";
public string Code {get;set;}="";
public ICollection<Plan>
Plans {get;set;}
=new();
}
六、Plan套餐模型
例如:
Personal
Professional
Enterprise
代码:
public class Plan
{
public Guid Id {get;set;}
public Guid ProductId {get;set;}
public Product Product {get;set;}=null!;
public string Name {get;set;}="";
public decimal Price {get;set;}
public int MaxDevices {get;set;}
public int ValidDays {get;set;}
}
七、License授权模型
这是核心表。
public class License
{
public Guid Id {get;set;}
public string LicenseKeyHash {get;set;}="";
public Guid UserId {get;set;}
public User User {get;set;}=null!;
public Guid PlanId {get;set;}
public Plan Plan {get;set;}=null!;
public int MaxDevices {get;set;}
public DateTime ExpireAt {get;set;}
public bool Enabled {get;set;}
public DateTime CreatedAt {get;set;}
=DateTime.UtcNow;
public ICollection<Device>
Devices {get;set;}
=new();
}
八、Device设备绑定模型
用于:
限制:
1台 / 3台 / 5台。
public class Device
{
public Guid Id {get;set;}
public Guid LicenseId {get;set;}
public License License {get;set;}=null!;
public string Fingerprint {get;set;}="";
public string Platform {get;set;}="";
public string DeviceName {get;set;}="";
public DateTime LastHeartbeat {get;set;}
public DateTime CreatedAt {get;set;}
=DateTime.UtcNow;
}
九、订单模型
public class Order
{
public Guid Id {get;set;}
public string OrderNo {get;set;}="";
public Guid UserId {get;set;}
public decimal Amount {get;set;}
public string Currency {get;set;}="USD";
public string Status {get;set;}="Pending";
public DateTime CreatedAt {get;set;}
=DateTime.UtcNow;
}
十、Payment支付记录
public class Payment
{
public Guid Id {get;set;}
public Guid OrderId {get;set;}
public string Provider {get;set;}="Stripe";
public string TransactionId {get;set;}="";
public string Status {get;set;}="";
public DateTime CreatedAt {get;set;}
}
十一、AuditLog审计日志
商业后台必须有。
public class AuditLog
{
public Guid Id {get;set;}
public Guid? UserId {get;set;}
public string Action {get;set;}="";
public string IpAddress {get;set;}="";
public string Detail {get;set;}="";
public DateTime CreatedAt {get;set;}
=DateTime.UtcNow;
}
十二、DbContext完整实现
文件:
License.Infrastructure/Persistence/LicenseDbContext.cs
using Microsoft.EntityFrameworkCore;
using License.Domain.Entities;
public class LicenseDbContext
:
DbContext
{
public LicenseDbContext(
DbContextOptions options)
:base(options)
{
}
public DbSet<User> Users=>Set<User>();
public DbSet<Role> Roles=>Set<Role>();
public DbSet<Permission> Permissions=>Set<Permission>();
public DbSet<Product> Products=>Set<Product>();
public DbSet<Plan> Plans=>Set<Plan>();
public DbSet<License> Licenses=>Set<License>();
public DbSet<Device> Devices=>Set<Device>();
public DbSet<Order> Orders=>Set<Order>();
public DbSet<Payment> Payments=>Set<Payment>();
public DbSet<AuditLog> AuditLogs=>Set<AuditLog>();
protected override void OnModelCreating(
ModelBuilder builder)
{
base.OnModelCreating(builder);
}
}
十三、Fluent API配置
创建:
Configurations
例如:
UserConfiguration.cs
代码:
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Builders;
public class UserConfiguration
:
IEntityTypeConfiguration<User>
{
public void Configure(
EntityTypeBuilder<User> builder)
{
builder
.HasIndex(x=>x.Email)
.IsUnique();
builder
.Property(x=>x.Email)
.HasMaxLength(256);
}
}
十四、注册所有配置
DbContext:
加入:
protected override void OnModelCreating(
ModelBuilder builder)
{
builder.ApplyConfigurationsFromAssembly(
typeof(LicenseDbContext)
.Assembly);
}
十五、安装EF Migration工具
全局:
dotnet tool install \
--global dotnet-ef
检查:
dotnet ef --version
十六、创建第一次Migration
进入:
cd src/Server
执行:
dotnet ef migrations add InitialCreate \
-p License.Infrastructure \
-s License.Api
生成:
Migrations/
20260811_InitialCreate.cs
十七、更新数据库
开发环境:
dotnet ef database update \
-p License.Infrastructure \
-s License.Api
数据库:
生成:
Users
Roles
Licenses
Devices
Orders
Payments
十八、Repository模式
为什么需要?
不要:
Controller直接操作DbContext。
错误:
_db.Users.FirstAsync();
正确:
Controller
↓
Service
↓
Repository
↓
DbContext
十九、Repository接口
Application:
创建:
Interfaces/IRepository.cs
public interface IRepository<T>
where T:class
{
Task<T?> GetAsync(Guid id);
Task<List<T>> GetAllAsync();
Task AddAsync(T entity);
void Remove(T entity);
}
二十、Repository实现
Infrastructure:
public class Repository<T>
:
IRepository<T>
where T:class
{
private readonly LicenseDbContext _db;
public Repository(
LicenseDbContext db)
{
_db=db;
}
public async Task<T?> GetAsync(
Guid id)
{
return await _db.Set<T>()
.FindAsync(id);
}
public async Task<List<T>>
GetAllAsync()
{
return await _db.Set<T>()
.ToListAsync();
}
public async Task AddAsync(
T entity)
{
await _db.Set<T>()
.AddAsync(entity);
}
public void Remove(
T entity)
{
_db.Remove(entity);
}
}
二十一、Unit Of Work
接口:
public interface IUnitOfWork
{
IRepository<User> Users {get;}
IRepository<License> Licenses {get;}
Task<int> SaveAsync();
}
二十二、实现UnitOfWork
public class UnitOfWork
:
IUnitOfWork
{
private readonly LicenseDbContext _db;
public IRepository<User> Users {get;}
public IRepository<License> Licenses {get;}
public UnitOfWork(
LicenseDbContext db)
{
_db=db;
Users=
new Repository<User>(db);
Licenses=
new Repository<License>(db);
}
public Task<int>
SaveAsync()
{
return _db.SaveChangesAsync();
}
}
二十三、注册DI
Infrastructure:
services.AddScoped(
typeof(IRepository<>),
typeof(Repository<>)
);
services.AddScoped
<IUnitOfWork,
UnitOfWork>();
二十四、数据库Seed初始化
创建:
DatabaseSeeder.cs
初始化:
默认管理员:
admin@example.com
Role:
SuperAdmin
二十五、本阶段完成
| 模块 | 状态 |
|---|---|
| 完整数据库模型 | ✅ |
| EF Core实体 | ✅ |
| Fluent API | ✅ |
| Migration | ✅ |
| PostgreSQL结构 | ✅ |
| Repository | ✅ |
| UnitOfWork | ✅ |
| RBAC基础 | ✅ |
| Seed设计 | ✅ |
下一章节
第十(四):License Server源码实现
下一部分开始实现真正核心:
License Engine
包括:
- License Key生成算法
例如:
48D0-61CD-35FE...
- ECDSA数字签名
- Private Key保护
- Public Key客户端验证
- 激活流程
- 设备指纹算法
- 在线激活API
- 离线License文件
- Heartbeat机制
- 防重放攻击
完成后,这套系统才真正拥有商业软件授权能力。
LicenseServer 商业授权系统开发实战文档 v1.0
第十部分:完整源码工程实现阶段
第十(四):License Server 源码实现
本阶段实现整个系统最核心模块:
License Engine(授权引擎)
这是商业软件授权系统的核心竞争力。
目标:
实现:
- License Key生成
- License数据签名
- 激活验证
- 设备绑定
- 多设备限制
- 在线授权
- 离线授权
- Heartbeat检测
- 防重放攻击
- 防篡改验证
最终流程:
Admin Console
|
|
Create License
|
|
License Server
|
+--------+---------+
| |
Database Private Key
|
|
Signed License
|
|
Client SDK
|
Public Key Verify
一、License设计原则
商业授权不能简单:
Key = ABC123
因为:
攻击者可以:
- 修改客户端
- 破解判断逻辑
- 生成假Key
采用:
非对称密码体系
服务器:
保存:
Private Key
负责:
签名。
客户端:
保存:
Public Key
负责:
验证。
关系:
Private Key
|
|
Signature
|
|
License Data
|
|
Public Key Verify
二、License数据结构设计
创建:
License.Domain/Models/LicensePayload.cs
代码:
namespace License.Domain.Models;
public class LicensePayload
{
public string LicenseId {get;set;}="";
public string Product {get;set;}="";
public string Plan {get;set;}="";
public string UserEmail {get;set;}="";
public int MaxDevices {get;set;}
public DateTime IssuedAt {get;set;}
public DateTime ExpireAt {get;set;}
public string[] Features {get;set;}=[];
}
三、最终License格式
用户看到:
48D0-61CD-35FE-9C9B-5358-3725-D644-1A18
但是内部:
不是直接保存。
实际:
{
"id":
"7d8a....",
"product":
"MyEditor",
"plan":
"Professional",
"maxDevices":
3,
"expire":
"2027-01-01",
"signature":
"MEUCIQ..."
}
四、License Key生成
不要使用:
Random()
原因:
可预测。
使用:
Cryptographically Secure Random
代码:
创建:
License.Infrastructure/Security/LicenseKeyGenerator.cs
using System.Security.Cryptography;
public class LicenseKeyGenerator
{
public string Generate()
{
byte[] buffer =
RandomNumberGenerator
.GetBytes(16);
string key =
Convert.ToHexString(buffer);
return Format(key);
}
private string Format(
string input)
{
return string.Join(
"-",
Enumerable.Range(0, input.Length / 4)
.Select(
i=>input.Substring(i*4,4))
);
}
}
生成:
例如:
A81F-92CC-17DA-4B55-9F31-88AA-92F1-22DE
五、License Hash存储
数据库:
不要保存明文Key。
错误:
LicenseKey
48D0-61CD...
正确:
保存:
SHA256(Key)
原因:
数据库泄露时:
攻击者无法直接使用。
创建:
LicenseHasher.cs
代码:
using System.Security.Cryptography;
using System.Text;
public class LicenseHasher
{
public string Hash(
string key)
{
using var sha =
SHA256.Create();
byte[] data =
Encoding.UTF8
.GetBytes(key);
return Convert
.ToHexString(
sha.ComputeHash(data));
}
}
六、ECDSA密钥生成
算法:
ECDSA P-256
生成:
工具:
openssl ecparam \
-name prime256v1 \
-genkey \
-noout \
-out private.pem
导出公钥:
openssl ec \
-in private.pem \
-pubout \
-out public.pem
最终:
服务器:
private.pem
客户端:
public.pem
七、License签名服务
创建:
Security/LicenseSigner.cs
代码:
using System.Security.Cryptography;
using System.Text;
using System.Text.Json;
public class LicenseSigner
{
private readonly ECDsa _ecdsa;
public LicenseSigner(
ECDsa ecdsa)
{
_ecdsa=ecdsa;
}
public string Sign(
LicensePayload payload)
{
string json =
JsonSerializer.Serialize(payload);
byte[] data =
Encoding.UTF8
.GetBytes(json);
byte[] signature =
_ecdsa.SignData(
data,
HashAlgorithmName.SHA256);
return Convert
.ToBase64String(
signature);
}
}
八、License验证
客户端:
使用:
Public Key。
代码:
public bool Verify(
string json,
string signature)
{
byte[] data =
Encoding.UTF8.GetBytes(json);
byte[] sign =
Convert.FromBase64String(
signature);
return _ecdsa.VerifyData(
data,
sign,
HashAlgorithmName.SHA256);
}
九、License创建流程
Admin点击:
Create License
流程:
Admin
|
|
POST /admin/license/create
|
|
Generate Key
|
|
Create Payload
|
|
Sign Payload
|
|
Save Hash
|
|
Return Key
十、创建License Service
Application:
创建:
Services/LicenseIssuerService.cs
代码:
public class LicenseIssuerService
{
private readonly LicenseKeyGenerator _generator;
private readonly LicenseSigner _signer;
public async Task<string>
CreateAsync(
LicensePayload payload)
{
var key =
_generator.Generate();
var signature =
_signer.Sign(payload);
return key;
}
}
十一、激活API设计
客户端首次启动:
发送:
POST
/api/license/activate
请求:
{
"licenseKey":
"48D0-61CD...",
"deviceFingerprint":
"xxxx"
}
十二、Activation Service
创建:
ActivationService.cs
逻辑:
1. Hash License Key
2. 查询数据库
3. 判断是否存在
4. 判断是否过期
5. 查询设备数量
6. 新增设备
7. 返回License Payload
代码框架:
public async Task<Result>
Activate(
ActivateRequest request)
{
var hash =
_hasher.Hash(
request.LicenseKey);
var license =
await repo.Find(hash);
if(license==null)
return Failed();
if(
license.Devices.Count
>=
license.MaxDevices)
return DeviceLimit();
AddDevice();
return Success();
}
十三、设备指纹算法
目标:
识别:
同一电脑。
不能:
只使用MAC地址。
因为:
容易变化。
组合:
Windows:
MachineGuid
CPU ID
Disk Serial
MAC
macOS:
Hardware UUID
System Serial
Linux:
/etc/machine-id
CPU
Disk UUID
最终:
Hash:
SHA256(
machineInfo
)
十四、Device Fingerprint实现
SDK:
public class DeviceFingerprint
{
public string Generate()
{
string raw =
GetMachineId()
+
GetCpuId();
return SHA256Hash(raw);
}
}
十五、Heartbeat机制
目的:
防止:
复制License文件。
客户端:
每天:
或者启动:
调用:
POST
/api/license/heartbeat
数据:
{
licenseId:
"xxx",
deviceId:
"xxx",
version:
"1.0"
}
服务器更新:
LastHeartbeat
十六、防重放攻击
攻击:
截获:
激活请求。
解决:
加入:
Nonce。
请求:
{
licenseKey:"",
nonce:"A82FD91",
timestamp:123456
}
服务器:
保存:
最近Nonce。
检查:
是否使用过
是否超时
十七、离线授权
场景:
工业软件:
无网络。
流程:
服务器生成:
license.dat
内容:
{
payload:{},
signature:"xxx"
}
客户端:
只验证:
签名。
十八、License API Controller
创建:
Controllers/LicenseController.cs
增加:
[HttpPost("activate")]
public async Task<IActionResult>
Activate(
ActivateRequest request)
{
var result =
await service.ActivateAsync(
request);
return Ok(result);
}
十九、缓存策略
Redis缓存:
适合:
频繁验证。
例如:
license:{hash}
TTL:
5分钟
二十、安全增强
生产环境必须:
Private Key
禁止:
放:
appsettings.json
使用:
- Hashicorp Vault
- AWS KMS
- Azure Key Vault
API限流
例如:
同IP:
5分钟100次
超过:
封禁。
二十一、本阶段完成
| 模块 | 状态 |
|---|---|
| License Key生成 | ✅ |
| SHA256存储 | ✅ |
| ECDSA签名 | ✅ |
| Public Key验证 | ✅ |
| 激活流程 | ✅ |
| 设备限制 | ✅ |
| Heartbeat | ✅ |
| 离线授权 | ✅ |
| 防重放设计 | ✅ |
| Redis缓存 | ✅ |
| 密钥保护方案 | ✅ |
当前系统进度
目前:
数据库
+
Backend API
+
License Engine
=
商业授权服务器核心完成
下一章节
第十(五):Client SDK生产代码实现
下一部分将实现:
客户端真正接入:
包括:
- LicenseManager
- License缓存
- AES本地加密
- ECDSA验证
- Device Fingerprint
- Activation Client
- Heartbeat Client
- Offline License加载
- 自动更新接口
- SDK NuGet打包
完成后:
任何.NET软件都可以:
LicenseManager.Initialize();
LicenseManager.Activate(key);
直接接入你的商业授权平台。
目前整个《LicenseServer 商业授权系统开发实战》已经进入第十部分源码实现阶段。
按照最初规划,后续还有几个核心章节。
目前进度:
| 章节 | 内容 | 状态 |
|---|---|---|
| 第九(一)~(十) | 完整商业架构设计 | ✅ 已完成 |
| 第十(一) | Monorepo工程初始化 | ✅ 已完成 |
| 第十(二) | ASP.NET Core Backend基础实现 | ✅ 已完成 |
| 第十(三) | 数据库 + EF Core + Repository | ✅ 已完成 |
| 第十(四) | License Engine核心实现 | ✅ 已完成 |
| 第十(五) | Client SDK实现 | ⬅️ 下一章 |
| 第十(六) | Admin Console完整实现 | 待完成 |
| 第十(七) | 支付系统+自动发货实现 | 待完成 |
| 第十(八) | Docker生产部署+CI/CD实现 | 待完成 |
| 第十(九) | 安全加固与反破解 | 待完成 |
| 第十(十) | 商业发行与最终交付 | 待完成 |
还剩多少?
如果按照当前路线:
还剩 6 个大章节:
第十(五):Client SDK生产实现
这是下一章。
主要代码:
.NET SDK
实现:
License.Client.SDK
├── LicenseManager
├── ActivationClient
├── LicenseValidator
├── DeviceFingerprint
├── Crypto
├── Storage
└── HeartbeatClient
完成后:
你的软件可以:
var license =
await LicenseManager.ActivateAsync(
"48D0-61CD..."
);
if(license.Valid)
{
StartApplication();
}
第十(六):Admin Console完整实现
实现后台管理系统。
技术:
Next.js 16
+
TypeScript
+
Tailwind
+
shadcn/ui
+
React Query
页面:
Dashboard
用户管理
License管理
设备管理
订单管理
支付管理
日志审计
系统设置
第十(七):支付 + 自动交付闭环
实现:
用户:
购买软件
↓
Stripe付款
↓
Webhook通知
↓
创建License
↓
发送邮件
↓
用户下载软件
↓
自动激活
包括:
- Stripe API
- PayPal扩展设计
- 邮件系统
- 下载中心
- License自动生成
第十(八):生产部署
实现:
服务器:
Ubuntu/Debian
Docker
Nginx
Cloudflare
PostgreSQL
Redis
Vault
包括:
- docker-compose生产版
- HTTPS
- 自动备份
- 数据恢复
- 日志系统
- 监控
第十(九):安全增强
这是商业软件最重要部分。
包括:
服务端安全
- API Rate Limit
- WAF
- SQL防护
- 密钥轮换
客户端安全
- 混淆
- 防调试
- 防Patch
- 完整性检查
- 代码签名
高级:
- TPM绑定
- Hardware Key
- HSM
第十(十):商业发行最终方案
最后收尾:
包括:
软件发行
Windows:
MSIX
Installer
Code Signing
macOS:
Developer ID
Notarization
DMG
Linux:
AppImage
Deb
RPM
商业运营
包括:
- 用户体系
- 授权策略
- 客服流程
- 数据统计
- 版本升级
完成后最终成果
最终会形成:
LicensePlatform
├── License Server
│
├── ASP.NET Core API
├── PostgreSQL
├── Redis
├── Vault
├── Admin Console
├── Client SDK
├── Payment System
├── Auto Delivery
├── CI/CD
└── Deployment
预计剩余工作量
按照现在详细程度:
| 部分 | 预计长度 |
|---|---|
| 第十(五)SDK | 约1~2次回复 |
| 第十(六)Admin | 约2~3次回复 |
| 第十(七)支付系统 | 约2次回复 |
| 第十(八)部署 | 约2次回复 |
| 第十(九)安全 | 约2~3次回复 |
| 第十(十)发行 | 约1~2次回复 |
大约:
还有 10~14 次左右内容更新可以完成整个商业版本文档。
下一步继续:
第十(五):Client SDK生产代码实现
这一章非常关键,因为它决定:
你的软件如何安全地接入授权系统,以及如何抵抗简单破解。
会开始写真正客户端代码。
第十(五):Client SDK生产代码实现 继续
LicenseServer 商业授权系统开发实战文档 v1.0
第十部分:完整源码工程实现阶段
第十(五):Client SDK 生产代码实现
本章节实现:
面向 Windows / macOS / Linux 软件的商业授权客户端 SDK。
目标:
任何你的软件项目,只需要引用:
License.Client.SDK.dll
即可接入授权系统。
最终开发体验:
using License.Client;
var result =
await LicenseManager.ActivateAsync(
"48D0-61CD-35FE-9C9B"
);
if(result.Success)
{
StartApplication();
}
一、Client SDK设计目标
商业SDK必须满足:
| 能力 | 实现 |
|---|---|
| 在线激活 | ✅ |
| 离线License | ✅ |
| 设备绑定 | ✅ |
| License验证 | ✅ |
| 签名验证 | ✅ |
| 本地缓存 | ✅ |
| 加密存储 | ✅ |
| Heartbeat | ✅ |
| 版本检查 | ✅ |
| 跨平台 | ✅ |
支持:
- Windows
- macOS
- Linux
.NET支持:
.NET 8+
.NET 10
二、SDK工程结构
项目:
SDK/
License.Client.SDK/
最终:
License.Client.SDK/
├── LicenseManager.cs
├── ActivationClient.cs
├── HeartbeatClient.cs
├── LicenseValidator.cs
├── DeviceFingerprint.cs
├── LicenseStorage.cs
├── OfflineLicenseReader.cs
├── Models/
│
├── LicenseInfo.cs
├── ActivationResult.cs
├── LicenseResponse.cs
├── Crypto/
│
├── EcdsaVerifier.cs
├── AesEncryptor.cs
├── Network/
│
└── ApiClient.cs
└── Exceptions/
三、SDK核心调用流程
软件启动:
Application
|
|
LicenseManager
|
+----------+
| |
Online Offline
|
Activation API
|
License Server
四、创建SDK项目
进入:
cd src/SDK
创建:
dotnet new classlib \
-n License.Client.SDK
安装:
HTTP:
dotnet add package System.Net.Http.Json
加密:
.NET内置:
无需安装。
五、License模型
路径:
Models/LicenseInfo.cs
代码:
namespace License.Client.Models;
public class LicenseInfo
{
public string LicenseId
{
get;set;
}=""
public string Product
{
get;set;
}=""
public string Plan
{
get;set;
}=""
public int MaxDevices
{
get;set;
}
public DateTime ExpireAt
{
get;set;
}
public bool Valid
{
get;set;
}
public string Signature
{
get;set;
}=""
}
六、ActivationResult
namespace License.Client.Models;
public class ActivationResult
{
public bool Success
{
get;set;
}
public string Message
{
get;set;
}=""
public LicenseInfo?
License
{
get;set;
}
}
七、Device Fingerprint实现
这是设备绑定核心。
文件:
DeviceFingerprint.cs
设计:
不要使用单一硬件信息。
组合:
Windows:
MachineGuid
CPU
BIOS
Disk
Linux:
/etc/machine-id
CPU
macOS:
Hardware UUID
Serial
代码:
using System.Security.Cryptography;
using System.Text;
namespace License.Client;
public class DeviceFingerprint
{
public static string Generate()
{
var raw =
Environment.MachineName
+
Environment.OSVersion
+
Environment.ProcessorCount;
using var sha =
SHA256.Create();
var hash =
sha.ComputeHash(
Encoding.UTF8.GetBytes(raw));
return Convert
.ToHexString(hash);
}
}
生产版本:
后续增加:
- WMI
- IOKit
- Linux DMI
八、本地License存储
不能:
直接:
license.json
因为用户可以修改。
采用:
AES-256-GCM
+
Machine Key
结构:
~/.license/
license.dat
九、AES加密工具
文件:
Crypto/AesEncryptor.cs
代码:
using System.Security.Cryptography;
using System.Text;
namespace License.Client.Crypto;
public class AesEncryptor
{
public static byte[] Encrypt(
string text,
byte[] key)
{
using var aes =
Aes.Create();
aes.Key=key;
aes.GenerateIV();
using var encryptor =
aes.CreateEncryptor();
var data =
Encoding.UTF8
.GetBytes(text);
var encrypted =
encryptor.TransformFinalBlock(
data,
0,
data.Length);
return aes.IV
.Concat(encrypted)
.ToArray();
}
}
十、本地License Storage
文件:
LicenseStorage.cs
代码:
using License.Client.Crypto;
namespace License.Client;
public class LicenseStorage
{
private readonly string path;
public LicenseStorage()
{
path =
Path.Combine(
Environment.GetFolderPath(
Environment.SpecialFolder.ApplicationData),
"license.dat");
}
public void Save(
string data)
{
File.WriteAllText(
path,
data);
}
public string?
Load()
{
if(!File.Exists(path))
return null;
return File.ReadAllText(path);
}
}
十一、ECDSA验证
客户端:
只有:
Public Key。
文件:
Crypto/EcdsaVerifier.cs
代码:
using System.Security.Cryptography;
using System.Text;
namespace License.Client.Crypto;
public class EcdsaVerifier
{
public bool Verify(
string payload,
string signature,
string publicKey)
{
using var ecdsa =
ECDsa.Create();
ecdsa.ImportFromPem(
publicKey);
return ecdsa.VerifyData(
Encoding.UTF8.GetBytes(payload),
Convert.FromBase64String(signature),
HashAlgorithmName.SHA256
);
}
}
十二、API Client
文件:
Network/ApiClient.cs
代码:
using System.Net.Http.Json;
namespace License.Client.Network;
public class ApiClient
{
private readonly HttpClient client;
public ApiClient(
string endpoint)
{
client =
new HttpClient()
{
BaseAddress =
new Uri(endpoint)
};
}
public async Task<T?>
PostAsync<T>(
string url,
object data)
{
var response =
await client.PostAsJsonAsync(
url,
data);
return await response
.Content
.ReadFromJsonAsync<T>();
}
}
十三、Activation Client
文件:
ActivationClient.cs
代码:
using License.Client.Models;
namespace License.Client;
public class ActivationClient
{
private readonly ApiClient api;
public ActivationClient(
ApiClient api)
{
this.api=api;
}
public async Task<ActivationResult?>
ActivateAsync(
string key)
{
var fingerprint =
DeviceFingerprint.Generate();
return await api
.PostAsync<ActivationResult>(
"/api/license/activate",
new
{
licenseKey=key,
deviceFingerprint=fingerprint
});
}
}
十四、Heartbeat Client
文件:
HeartbeatClient.cs
代码:
namespace License.Client;
public class HeartbeatClient
{
public async Task SendAsync(
LicenseInfo license)
{
// 调用服务器heartbeat API
}
}
十五、LicenseManager核心入口
这是开发者使用接口。
文件:
LicenseManager.cs
代码:
using License.Client.Models;
namespace License.Client;
public static class LicenseManager
{
private static LicenseInfo?
_current;
public static LicenseInfo?
Current
=>_current;
public static async Task<ActivationResult>
ActivateAsync(
string key)
{
var api =
new ApiClient(
"https://api.example.com");
var client =
new ActivationClient(api);
var result =
await client
.ActivateAsync(key);
if(result!=null &&
result.Success)
{
_current=
result.License;
}
return result!;
}
public static bool IsValid()
{
return _current!=null
&&
_current.Valid
&&
_current.ExpireAt>DateTime.UtcNow;
}
}
十六、软件接入方式
你的软件:
例如:
static void Main()
{
if(!LicenseManager.IsValid())
{
var result =
LicenseManager.ActivateAsync(
Console.ReadLine()
)
.Result;
if(!result.Success)
return;
}
StartProgram();
}
十七、NuGet发布准备
修改:
.csproj:
<PropertyGroup>
<PackageId>
Company.License.SDK
</PackageId>
<Version>
1.0.0
</Version>
<Authors>
Company
</Authors>
<GeneratePackageOnBuild>
true
</GeneratePackageOnBuild>
</PropertyGroup>
打包:
dotnet pack -c Release
生成:
Company.License.SDK.1.0.0.nupkg
十八、SDK安全增强建议(商业版)
目前:
基础版。
生产增强:
1. Public Key混淆
不要:
publicKey="xxxx"
改:
- 分段
- 加密字符串
- Runtime组合
2. 防Debug
检测:
Debugger.IsAttached
3. 完整性检查
验证:
自身DLL Hash。
4. License缓存加密
升级:
AES-GCM。
5. 防时间回拨
保存:
服务器时间。
十九、本阶段完成
| 模块 | 状态 |
|---|---|
| SDK架构 | ✅ |
| Activation Client | ✅ |
| LicenseManager | ✅ |
| 设备指纹 | ✅ |
| 本地存储 | ✅ |
| ECDSA验证 | ✅ |
| Heartbeat框架 | ✅ |
| NuGet打包 | ✅ |
| 跨平台设计 | ✅ |
当前整体进度
Backend
✅
Database
✅
License Engine
✅
Client SDK
✅
下一章节
第十(六):Admin Console 完整实现
下一章进入后台管理系统开发:
技术:
Next.js 16
+
TypeScript
+
Bun
+
Tailwind CSS
+
shadcn/ui
+
React Query
实现:
- 登录
- RBAC权限
- Dashboard
- 用户管理
- License生成
- License查询
- 设备管理
- 订单管理
- 审计日志
完成后,你将拥有完整的:
后台运营控制中心。
LicenseServer 商业授权系统开发实战文档 v1.0
第十部分:完整源码工程实现阶段
第十(六):Admin Console 完整实现
本章节实现:
商业授权系统管理员后台(Admin Console)
Admin Console 是整个商业系统运营入口。
管理员通过后台完成:
- 创建产品
- 管理用户
- 创建License
- 查看激活设备
- 管理订单
- 查看支付状态
- 风控审计
- 系统配置
一、技术架构
采用:
Next.js 16
+
TypeScript
+
Bun
+
Tailwind CSS
+
shadcn/ui
+
React Query
+
Zod
+
React Hook Form
架构:
Browser
|
Next.js App Router
|
React Components
|
API Client
|
ASP.NET Core Backend
二、项目结构
位置:
src/Admin/license-admin
最终结构:
license-admin/
src/
├── app/
│
├── login/
│ └── page.tsx
│
├── dashboard/
│ └── page.tsx
│
├── users/
│ └── page.tsx
│
├── licenses/
│ └── page.tsx
│
├── products/
│ └── page.tsx
│
├── orders/
│ └── page.tsx
├── settings/
│ └── page.tsx
├── components/
│
├── Sidebar.tsx
├── Header.tsx
├── DataTable.tsx
├── LicenseDialog.tsx
├── lib/
│
├── api.ts
├── auth.ts
├── utils.ts
├── hooks/
│
└── useLicense.ts
├── types/
└── license.ts
└── middleware.ts
三、安装依赖
进入:
cd src/Admin/license-admin
使用 Bun:
bun add axios
bun add @tanstack/react-query
bun add zod
bun add react-hook-form
bun add jose
UI:
初始化:
bunx shadcn@latest init
安装组件:
bunx shadcn@latest add \
button \
card \
table \
input \
dialog \
dropdown-menu \
badge \
form
四、环境变量
创建:
.env.local
内容:
NEXT_PUBLIC_API_URL=https://api.example.com
五、API Client
文件:
src/lib/api.ts
代码:
import axios from "axios";
export const api =
axios.create({
baseURL:
process.env
.NEXT_PUBLIC_API_URL,
withCredentials:true
});
api.interceptors.request.use(
(config)=>{
const token =
localStorage.getItem(
"token"
);
if(token)
{
config.headers.Authorization =
`Bearer ${token}`;
}
return config;
}
);
六、认证系统设计
流程:
Admin Login
|
|
POST /api/auth/login
|
|
JWT Token
|
|
localStorage
|
|
API Request
七、登录页面
文件:
app/login/page.tsx
代码:
"use client";
import {useState} from "react";
import {api} from "@/lib/api";
export default function Login()
{
const [email,setEmail]=useState("");
const [password,setPassword]=useState("");
async function login()
{
const res =
await api.post(
"/api/auth/login",
{
email,
password
});
localStorage.setItem(
"token",
res.data.token
);
location.href=
"/dashboard";
}
return (
<div>
<input
value={email}
onChange={
e=>setEmail(e.target.value)
}
/>
<input
type="password"
value={password}
onChange={
e=>setPassword(e.target.value)
}
/>
<button
onClick={login}
>
Login
</button>
</div>
);
}
八、后台布局 Layout
文件:
app/dashboard/layout.tsx
代码:
import Sidebar from
"@/components/Sidebar";
export default function Layout(
{
children
}:
{
children:React.ReactNode
}
)
{
return (
<div className="flex">
<Sidebar/>
<main className="flex-1 p-6">
{children}
</main>
</div>
)
}
九、Sidebar菜单
文件:
components/Sidebar.tsx
代码:
"use client";
import Link from "next/link";
export default function Sidebar()
{
return (
<nav>
<Link href="/dashboard">
Dashboard
</Link>
<Link href="/users">
Users
</Link>
<Link href="/licenses">
Licenses
</Link>
<Link href="/orders">
Orders
</Link>
<Link href="/settings">
Settings
</Link>
</nav>
)
}
十、Dashboard首页
显示:
- 用户数量
- License数量
- 收入
- 激活设备
文件:
app/dashboard/page.tsx
代码:
export default function Dashboard()
{
return (
<div>
<h1>
Dashboard
</h1>
<div className="grid grid-cols-4">
<Card>
Users
1000
</Card>
<Card>
Licenses
5000
</Card>
<Card>
Revenue
$20000
</Card>
<Card>
Devices
8000
</Card>
</div>
</div>
)
}
十一、License管理页面
功能:
管理员:
- 查询License
- 创建License
- 禁用License
- 查看设备
结构:
/licenses
GET
/api/admin/licenses
POST
/api/admin/licenses
DELETE
/api/admin/licenses/{id}
十二、License列表组件
"use client";
import {useQuery}
from
"@tanstack/react-query";
import {api}
from
"@/lib/api";
export default function Licenses()
{
const {
data
}=useQuery({
queryKey:[
"licenses"
],
queryFn:
async()=>{
const res=
await api.get(
"/api/admin/licenses"
);
return res.data;
}
});
return (
<table>
<tbody>
{
data?.map(
(item:any)=>(
<tr key={item.id}>
<td>
{item.licenseKey}
</td>
<td>
{item.status}
</td>
</tr>
)
)
}
</tbody>
</table>
)
}
十三、创建License弹窗
管理员输入:
用户
产品
套餐
设备数量
有效期
调用:
POST
/api/admin/licenses/create
组件:
components/LicenseDialog.tsx
十四、用户管理
页面:
/users
功能:
- 搜索用户
- 查看购买记录
- 查看License
- 禁用账户
API:
GET
/api/admin/users
十五、产品管理
产品:
例如:
Video Editor Pro
Image Tool
AI Assistant
字段:
{
"name":"Video Editor",
"code":"VIDEO_PRO"
}
十六、订单管理
页面:
/orders
显示:
| 字段 |
|---|
| 订单号 |
| 用户 |
| 金额 |
| 支付状态 |
| 创建时间 |
十七、RBAC权限控制
后台角色:
SuperAdmin
Admin
Support
Finance
权限:
license.create
license.delete
user.manage
payment.view
system.config
十八、前端权限组件
创建:
components/Permission.tsx
代码:
export function Permission(
{
allow,
children
}
)
{
const permissions =
JSON.parse(
localStorage.getItem(
"permissions"
) || "[]"
);
if(
!permissions.includes(allow)
)
return null;
return children;
}
十九、审计日志页面
管理员操作:
必须记录。
显示:
时间
用户
IP
操作
结果
API:
GET
/api/admin/audit
二十、安全设计
Admin Console必须:
1. 强制HTTPS
禁止:
HTTP。
2. 管理员MFA
推荐:
TOTP:
- Google Authenticator
- 1Password OTP
3. JWT短周期
推荐:
Access Token:
15分钟
Refresh Token:
7天
4. 登录保护
限制:
5次失败
↓
锁定15分钟
5. 操作审计
例如:
管理员删除License:
记录:
{
"user":"admin",
"action":
"DELETE_LICENSE",
"ip":
"1.2.3.4"
}
二十一、生产构建
开发:
bun dev
生产:
bun run build
启动:
bun start
二十二、Docker部署
Dockerfile:
FROM node:24-alpine
WORKDIR /app
COPY package.json .
RUN npm install
COPY . .
RUN npm run build
CMD [
"npm",
"start"
]
二十三、本阶段完成
| 模块 | 状态 |
|---|---|
| Next.js后台框架 | ✅ |
| 登录认证 | ✅ |
| JWT接入 | ✅ |
| Dashboard | ✅ |
| License管理 | ✅ |
| 用户管理 | ✅ |
| 订单管理 | ✅ |
| 权限系统 | ✅ |
| 审计日志 | ✅ |
| Docker支持 | ✅ |
当前系统进度
目前:
Backend ✅
Database ✅
License Engine ✅
Client SDK ✅
Admin Console ✅
已经接近一个完整商业授权平台。
下一章节
第十(七):支付系统 + 自动发货 + 商业闭环实现
下一章实现:
完整商业流程:
用户访问官网
↓
选择套餐
↓
Stripe付款
↓
Webhook通知
↓
创建订单
↓
生成License
↓
发送邮件
↓
用户下载软件
↓
客户端自动激活
包括:
- Stripe API
- Webhook安全验证
- 自动生成License
- Email系统
- 下载中心
- 订单状态机
- 退款处理
这一章完成后,你的软件授权系统就具备真正商业销售能力。
LicenseServer 商业授权系统开发实战文档 v1.0
第十部分:完整源码实现阶段
第十(七):支付系统 + 自动发货 + 商业闭环实现
本章节开始把前面的:
用户
↓
产品
↓
订单
↓
License
↓
客户端
真正串起来。
最终目标是实现:
用户购买软件
↓
创建订单
↓
第三方支付
↓
支付平台 Webhook
↓
服务器验证支付结果
↓
订单变为 Paid
↓
生成 License
↓
发送交付邮件
↓
用户下载客户端
↓
输入 License
↓
在线激活
这里有一个非常重要的原则:
绝不能根据浏览器前端返回的“支付成功”来发放 License。
只有服务器验证支付平台的可信 Webhook 后,才能把订单标记为已支付并触发发货。
一、商业支付架构
整体结构:
┌──────────────┐
│ Customer │
└──────┬───────┘
│
▼
┌──────────────┐
│ Web Store │
└──────┬───────┘
│
▼
┌──────────────┐
│ Order API │
└──────┬───────┘
│
▼
┌──────────────┐
│Payment Provider│
└──────┬───────┘
│
Webhook
│
▼
┌──────────────┐
│ Webhook API │
└──────┬───────┘
│
▼
┌─────────────────────┐
│ Order State Machine │
└──────────┬──────────┘
│
▼
┌──────────────┐
│License Engine│
└──────┬───────┘
│
┌───────────┴───────────┐
▼ ▼
Email Delivery Download Center
二、支付系统设计原则
商业系统中建议把:
Order
Payment
License
Delivery
严格分开。
不要设计成:
Payment = License
而应该:
Order
│
├── Payment
│
└── License
│
└── Devices
这样以后才能支持:
- 一次订单购买多个License
- 退款
- 补发License
- 更换License
- 订阅
- 升级套餐
- 企业订单
三、订单状态机
建议不要使用一个简单的:
Paid = true
而使用明确状态。
Pending
│
▼
PaymentPending
│
├──────────────┐
▼ ▼
Paid Failed
│
▼
Provisioning
│
▼
Delivered
│
▼
Completed
退款:
Paid
│
▼
RefundPending
│
▼
Refunded
四、OrderStatus
Domain:
Enums/OrderStatus.cs
namespace License.Domain.Enums;
public enum OrderStatus
{
Pending = 0,
PaymentPending = 1,
Paid = 2,
Provisioning = 3,
Delivered = 4,
Completed = 5,
Failed = 6,
RefundPending = 7,
Refunded = 8,
Cancelled = 9
}
五、PaymentStatus
namespace License.Domain.Enums;
public enum PaymentStatus
{
Pending = 0,
Succeeded = 1,
Failed = 2,
Refunded = 3,
PartiallyRefunded = 4
}
六、DeliveryStatus
新增:
Enums/DeliveryStatus.cs
namespace License.Domain.Enums;
public enum DeliveryStatus
{
Pending = 0,
Processing = 1,
Delivered = 2,
Failed = 3
}
七、重新设计Order实体
前面简单的Order需要升级。
using License.Domain.Enums;
namespace License.Domain.Entities;
public class Order
{
public Guid Id { get; set; }
public string OrderNo { get; set; } = "";
public Guid UserId { get; set; }
public User User { get; set; } = null!;
public decimal Amount { get; set; }
public string Currency { get; set; } = "USD";
public OrderStatus Status { get; set; }
= OrderStatus.Pending;
public DateTime CreatedAt { get; set; }
= DateTime.UtcNow;
public DateTime? PaidAt { get; set; }
public ICollection<Payment> Payments { get; set; }
= new List<Payment>();
public ICollection<License> Licenses { get; set; }
= new List<License>();
}
八、Payment实体升级
using License.Domain.Enums;
namespace License.Domain.Entities;
public class Payment
{
public Guid Id { get; set; }
public Guid OrderId { get; set; }
public Order Order { get; set; } = null!;
public string Provider { get; set; } = "";
public string ProviderPaymentId { get; set; } = "";
public PaymentStatus Status { get; set; }
= PaymentStatus.Pending;
public decimal Amount { get; set; }
public string Currency { get; set; } = "USD";
public DateTime CreatedAt { get; set; }
= DateTime.UtcNow;
public DateTime? CompletedAt { get; set; }
}
九、Webhook必须单独建立事件表
这是商业系统非常重要的一步。
为什么?
支付平台可能重复发送同一个Webhook。
例如:
Webhook #1
Webhook #1
Webhook #1
如果你的程序每次都:
生成License
最终用户可能得到多个License。
所以必须实现:
Idempotency(幂等处理)
创建:
PaymentWebhookEvent.cs
namespace License.Domain.Entities;
public class PaymentWebhookEvent
{
public Guid Id { get; set; }
public string Provider { get; set; } = "";
public string EventId { get; set; } = "";
public string EventType { get; set; } = "";
public DateTime ReceivedAt { get; set; }
= DateTime.UtcNow;
public DateTime? ProcessedAt { get; set; }
public bool Processed { get; set; }
}
数据库建立唯一索引:
Provider + EventId
这样同一个支付事件无论收到多少次,只处理一次。
十、支付Provider抽象
不要把业务代码直接写成:
Stripe.xxx();
否则以后换支付渠道会非常麻烦。
定义:
Application/Interfaces/IPaymentProvider.cs
public interface IPaymentProvider
{
Task<PaymentCheckoutResult>
CreateCheckoutAsync(
PaymentCheckoutRequest request);
Task<bool>
VerifyWebhookAsync(
string payload,
string signature);
}
十一、Checkout Request
public sealed class PaymentCheckoutRequest
{
public string OrderNo { get; init; } = "";
public decimal Amount { get; init; }
public string Currency { get; init; } = "USD";
public string CustomerEmail { get; init; } = "";
public string ProductName { get; init; } = "";
}
十二、Checkout Result
public sealed class PaymentCheckoutResult
{
public string ProviderPaymentId { get; init; } = "";
public string CheckoutUrl { get; init; } = "";
}
十三、创建订单流程
客户端/网站:
POST /api/orders
请求:
{
"planId": "xxxxxxxx",
"quantity": 1
}
服务器:
验证用户
↓
验证Product
↓
验证Plan
↓
计算服务器价格
↓
创建Order
↓
创建Checkout Session
↓
返回Checkout URL
注意:
价格必须由服务器决定。
不能相信浏览器传过来的:
{
"price": 0.01
}
否则攻击者可以修改前端请求。
十四、OrderService
public async Task<string> CreateOrderAsync(
CreateOrderRequest request,
Guid userId)
{
var plan =
await _plans.GetAsync(request.PlanId);
if (plan is null)
throw new InvalidOperationException(
"Plan not found.");
var order = new Order
{
Id = Guid.NewGuid(),
OrderNo = GenerateOrderNo(),
UserId = userId,
Amount = plan.Price,
Currency = "USD",
Status =
OrderStatus.PaymentPending
};
await _orders.AddAsync(order);
await _unitOfWork.SaveAsync();
var checkout =
await _paymentProvider
.CreateCheckoutAsync(
new PaymentCheckoutRequest
{
OrderNo = order.OrderNo,
Amount = order.Amount,
Currency = order.Currency,
ProductName = plan.Name
});
return checkout.CheckoutUrl;
}
十五、订单号生成
不要直接使用数据库ID作为订单号。
例如:
LP-20260811-8F3A91C2
代码:
private static string GenerateOrderNo()
{
return
$"LP-{DateTime.UtcNow:yyyyMMdd}-" +
$"{RandomNumberGenerator
.GetBytes(4)
.ToHexString()}";
}
生产环境还应该保证数据库唯一约束。
十六、Webhook API
创建:
Controllers/PaymentWebhookController.cs
[ApiController]
[Route("api/payment/webhook")]
public class PaymentWebhookController
: ControllerBase
{
private readonly IPaymentWebhookService _service;
public PaymentWebhookController(
IPaymentWebhookService service)
{
_service = service;
}
[HttpPost]
public async Task<IActionResult> Receive()
{
using var reader =
new StreamReader(
Request.Body);
var payload =
await reader.ReadToEndAsync();
var signature =
Request.Headers["X-Payment-Signature"]
.ToString();
await _service.ProcessAsync(
payload,
signature);
return Ok();
}
}
实际接入具体支付平台时,应使用该平台官方SDK/签名验证机制,而不是自行重新实现签名算法。
十七、Webhook处理流程
服务器收到:
Webhook
↓
读取原始Body
↓
验证签名
↓
取得Event ID
↓
查询WebhookEvent
↓
是否已经处理?
├── 是 → 直接返回200
│
└── 否
↓
开启数据库事务
↓
更新Payment
↓
更新Order
↓
创建License
↓
创建Delivery任务
↓
保存WebhookEvent
↓
Commit
十八、非常重要:事务边界
不要:
Webhook
↓
更新订单
↓
调用邮件服务器
↓
调用License服务器
↓
...
全部放在一个数据库事务里面。
因为外部网络操作可能超时。
推荐:
数据库事务
Order = Paid
Payment = Succeeded
License = Provisioning
DeliveryJob = Pending
Commit
然后:
Worker
↓
DeliveryJob
↓
生成License
↓
发送邮件
↓
更新DeliveryStatus
这就是:
Outbox / Background Job 模式。
十九、Delivery实体
public class Delivery
{
public Guid Id { get; set; }
public Guid OrderId { get; set; }
public Guid? LicenseId { get; set; }
public DeliveryStatus Status { get; set; }
= DeliveryStatus.Pending;
public int Attempts { get; set; }
public DateTime CreatedAt { get; set; }
= DateTime.UtcNow;
public DateTime? DeliveredAt { get; set; }
public string? Error { get; set; }
}
二十、自动发货Worker
你的项目已经有:
License.Worker
这里正式使用。
工作流程:
Delivery
↓
Pending
↓
Worker获取任务
↓
创建License
↓
写入数据库
↓
发送交付邮件
↓
Delivered
二十一、为什么不能直接在Webhook生成License?
例如:
Webhook()
{
CreateLicense();
SendEmail();
}
不推荐。
因为:
- Webhook可能重复
- 邮件可能失败
- License生成可能失败
- Webhook请求可能超时
- 支付平台可能重试
更好的:
Webhook
↓
数据库记录
↓
Worker
二十二、License自动发放
Worker:
public async Task ProcessDeliveryAsync(
Delivery delivery)
{
delivery.Status =
DeliveryStatus.Processing;
await _unitOfWork.SaveAsync();
try
{
var license =
await _licenseIssuer
.IssueAsync(
delivery.OrderId);
delivery.LicenseId =
license.Id;
await _emailService
.SendLicenseAsync(
license);
delivery.Status =
DeliveryStatus.Delivered;
delivery.DeliveredAt =
DateTime.UtcNow;
}
catch (Exception ex)
{
delivery.Attempts++;
delivery.Status =
DeliveryStatus.Failed;
delivery.Error =
ex.Message;
}
await _unitOfWork.SaveAsync();
}
二十三、失败重试
例如:
第一次失败
↓
1分钟后
第二次失败
↓
5分钟后
第三次失败
↓
30分钟后
第四次失败
↓
进入Dead Letter
不要无限重试。
推荐:
MaxAttempts = 5
二十四、Email系统
建议抽象:
public interface IEmailService
{
Task SendLicenseAsync(
License license);
Task SendOrderConfirmationAsync(
Order order);
Task SendRefundNotificationAsync(
Order order);
}
这样以后可以替换邮件服务商。
二十五、License交付邮件
邮件中建议包含:
订单号
产品名称
套餐
License Key
有效期
最大设备数
下载地址
账户管理地址
但是:
不要把数据库中的License私密数据直接全部输出到邮件。
只发送用户真正需要的信息。
二十六、License Key找回功能
你最初的需求中有:
用户填写邮箱,之后可以找回激活码。
商业系统建议:
不要提供:
输入邮箱
↓
直接显示全部License Key
因为邮箱一旦泄露,就可能成为授权泄露入口。
推荐:
用户输入邮箱
↓
发送一次性验证邮件
↓
用户验证
↓
进入License中心
↓
显示License状态
如果确实需要找回完整Key:
邮箱验证
+
一次性Token
+
短时间有效
二十七、License页面
用户登录:
/account/licenses
显示:
Video Editor Pro
License:
48D0-61CD-35FE-....
Status:
Active
Devices:
2 / 3
Expires:
2027-08-11
二十八、下载中心
推荐:
/downloads
例如:
Video Editor Pro
Windows
Version 1.0.0
macOS
Version 1.0.0
Linux
Version 1.0.0
每个文件:
服务器保存:
Version
Platform
Architecture
SHA256
Signature
ReleaseDate
客户端下载后:
下载
↓
SHA256验证
↓
数字签名验证
↓
安装
二十九、退款流程
退款不能简单:
Payment = Refunded
应该:
Refund Request
↓
Payment Provider
↓
Webhook
↓
Order = Refunded
↓
License = Revoked
但是这里要注意:
是否立即撤销License应该由你的商业退款政策决定。
例如:
付款后立即退款
→ 撤销License
部分退款
→ 按政策处理
人工退款
→ Admin确认后处理
三十、License撤销
增加:
public enum LicenseStatus
{
Active,
Expired,
Suspended,
Revoked
}
退款后:
LicenseStatus = Revoked
客户端下一次:
Heartbeat
服务器返回:
{
"valid": false,
"reason": "LicenseRevoked"
}
三十一、Webhook安全
必须满足:
1. HTTPS
https://api.example.com
2. 验证Webhook签名
3. 验证Event ID
4. 幂等处理
5. 数据库事务
6. 不信任前端支付结果
7. 不把Webhook秘密写入Git
三十二、支付密钥配置
禁止:
var secret =
"sk_live_xxxxx";
禁止:
appsettings.json
提交:
GitHub
应该:
Environment Variables
↓
Secret Manager
↓
Vault / Cloud Secret Manager
三十三、测试环境
开发阶段必须使用:
Test/Sandbox Environment
不要直接拿真实支付环境测试。
测试:
成功支付
失败支付
取消支付
Webhook重复
Webhook乱序
退款
部分退款
网络超时
三十四、关键测试
尤其测试:
Webhook重复
发送:
event_001
event_001
event_001
最终:
Order = Paid
License = 1
Delivery = 1
不能:
License = 3
三十五、支付闭环最终状态
完成后:
Customer
│
▼
Store Web
│
▼
Order
│
▼
Checkout
│
▼
Payment
│
Webhook
│
▼
Webhook Handler
│
▼
Order = Paid
│
▼
Delivery Queue
│
▼
License Engine
│
┌──────┴──────┐
▼ ▼
License Email
│
▼
Client Activation
三十六、本阶段完成
| 模块 | 状态 |
|---|---|
| 订单状态机 | ✅ |
| Payment模型 | ✅ |
| Webhook事件模型 | ✅ |
| 幂等处理 | ✅ |
| 支付Provider抽象 | ✅ |
| Checkout流程 | ✅ |
| 自动License发放 | ✅ |
| Delivery Worker | ✅ |
| 邮件交付 | ✅ |
| 退款流程 | ✅ |
| License撤销 | ✅ |
| 下载中心设计 | ✅ |
| 支付安全 | ✅ |
当前完整系统
现在已经形成:
LicensePlatform
│
┌───────────────────┼───────────────────┐
│ │ │
▼ ▼ ▼
Backend Admin Payment
│ │ │
▼ ▼ ▼
PostgreSQL Next.js Webhook
│ │
▼ ▼
Redis License
│ │
└───────────────────┬───────────────────┘
▼
Client SDK
│
┌────────┼────────┐
▼ ▼ ▼
Windows macOS Linux
下一章是:
第十(八):生产部署 + Docker + CI/CD + 数据库备份恢复
这一章会把目前的代码真正部署到生产服务器,并重点处理:
- Docker Compose
- Nginx
- HTTPS
- PostgreSQL生产配置
- Redis
- Worker
- Admin
- API
- 自动迁移
- 数据库备份
- 灾难恢复
- GitHub Actions
- Rolling/Blue-Green发布策略
- 日志与监控
- Secrets管理
完成第十(八)之后,整个系统就从“开发环境”正式进入可上线的生产环境阶段。
LicenseServer 商业授权系统开发实战 v1.0
第十部分:完整源码实现阶段
第十(八):生产部署 + Docker + CI/CD + 数据库备份恢复
这一章开始进入真正的生产环境部署阶段。
前面已经完成:
第十(一) 工程初始化
第十(二) ASP.NET Core Backend
第十(三) PostgreSQL + EF Core
第十(四) License Engine
第十(五) Client SDK
第十(六) Admin Console
第十(七) Payment + 自动发货
现在需要把它们部署成一个真正可以长期运行的商业系统。
最终生产架构:
Internet
│
▼
┌──────────────┐
│ Cloudflare │
└──────┬───────┘
│ HTTPS
▼
┌──────────────┐
│ Nginx │
└──────┬───────┘
│
┌─────────────┼─────────────┐
│ │ │
▼ ▼ ▼
Admin Web Backend API Download
Next.js ASP.NET Core Service
│
┌──────┴──────┐
│ │
▼ ▼
PostgreSQL Redis
│
▼
Backup Storage
▲
│
License Worker
Payment Worker
一、生产环境建议
对于你这个项目,我建议第一版商业上线采用:
OS:
Debian 13 / Ubuntu LTS
Container:
Docker Engine
Reverse Proxy:
Nginx
Database:
PostgreSQL
Cache:
Redis
Backend:
ASP.NET Core
Admin:
Next.js
Worker:
.NET Worker Service
CI/CD:
GitHub Actions
TLS:
Cloudflare + Let's Encrypt
Backup:
PostgreSQL + off-site storage
你的开发环境可以继续:
macOS
Windows
Linux
但生产环境建议尽量统一成:
Linux + Docker
这样可以显著降低环境差异。
二、生产目录结构
服务器:
/opt/license-platform/
建议:
/opt/license-platform/
├── compose/
│
│ ├── compose.prod.yml
│ ├── .env
│ └── nginx/
│
├── backups/
│
├── scripts/
│
│ ├── backup-db.sh
│ ├── restore-db.sh
│ └── deploy.sh
│
└── data/
注意:
.env
不要提交到 Git。
三、Docker服务划分
最终:
license-api
license-worker
license-admin
postgres
redis
nginx
如果下载服务器单独部署:
download-server
四、生产Docker Compose
文件:
deploy/docker/compose.prod.yml
示例:
services:
api:
image: ghcr.io/your-company/license-api:${VERSION}
restart: unless-stopped
environment:
ASPNETCORE_ENVIRONMENT: Production
ConnectionStrings__Default: ${DATABASE_CONNECTION}
Redis__ConnectionString: ${REDIS_CONNECTION}
depends_on:
postgres:
condition: service_healthy
redis:
condition: service_healthy
networks:
- backend
worker:
image: ghcr.io/your-company/license-worker:${VERSION}
restart: unless-stopped
environment:
ASPNETCORE_ENVIRONMENT: Production
ConnectionStrings__Default: ${DATABASE_CONNECTION}
Redis__ConnectionString: ${REDIS_CONNECTION}
depends_on:
postgres:
condition: service_healthy
networks:
- backend
admin:
image: ghcr.io/your-company/license-admin:${VERSION}
restart: unless-stopped
environment:
NODE_ENV: production
networks:
- frontend
postgres:
image: postgres:17
restart: unless-stopped
environment:
POSTGRES_DB: ${POSTGRES_DB}
POSTGRES_USER: ${POSTGRES_USER}
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
volumes:
- postgres_data:/var/lib/postgresql/data
healthcheck:
test:
[
"CMD-SHELL",
"pg_isready -U ${POSTGRES_USER}"
]
interval: 10s
timeout: 5s
retries: 5
networks:
- backend
redis:
image: redis:7-alpine
restart: unless-stopped
command:
[
"redis-server",
"--appendonly",
"yes"
]
volumes:
- redis_data:/data
healthcheck:
test:
[
"CMD",
"redis-cli",
"ping"
]
networks:
- backend
nginx:
image: nginx:alpine
restart: unless-stopped
ports:
- "80:80"
- "443:443"
volumes:
- ./nginx:/etc/nginx/conf.d:ro
- ./certs:/etc/nginx/certs:ro
depends_on:
- api
- admin
networks:
- frontend
- backend
volumes:
postgres_data:
redis_data:
networks:
frontend:
backend:
五、为什么PostgreSQL不能暴露到公网
错误:
Internet
│
▼
5432
│
PostgreSQL
绝对不要这样做。
正确:
Internet
│
▼
Nginx
│
▼
API
│
▼
PostgreSQL
PostgreSQL只监听Docker内部网络。
六、生产环境变量
服务器:
/opt/license-platform/compose/.env
例如:
VERSION=1.0.0
POSTGRES_DB=license_platform
POSTGRES_USER=license_app
POSTGRES_PASSWORD=CHANGE_THIS
DATABASE_CONNECTION=Host=postgres;Port=5432;Database=license_platform;Username=license_app;Password=CHANGE_THIS
REDIS_CONNECTION=redis:6379
ASPNETCORE_ENVIRONMENT=Production
但是商业生产环境更推荐:
Vault
AWS Secrets Manager
Azure Key Vault
GCP Secret Manager
而不是长期把数据库密码放在普通 .env 文件里。
七、数据库用户权限
不要让API使用:
postgres
超级管理员账户。
创建:
license_app
只授予业务数据库需要的权限。
例如:
CREATE USER license_app
WITH PASSWORD 'strong-password';
CREATE DATABASE license_platform;
GRANT CONNECT
ON DATABASE license_platform
TO license_app;
生产环境还应进一步限制 schema/table 权限。
八、ASP.NET Core Dockerfile
文件:
src/Backend/License.Api/Dockerfile
推荐多阶段构建:
FROM mcr.microsoft.com/dotnet/sdk:10.0 AS build
WORKDIR /src
COPY . .
RUN dotnet restore
RUN dotnet publish \
License.Api/License.Api.csproj \
-c Release \
-o /app/publish \
--no-restore
FROM mcr.microsoft.com/dotnet/aspnet:10.0 AS runtime
WORKDIR /app
COPY --from=build \
/app/publish .
ENV ASPNETCORE_URLS=http://+:8080
EXPOSE 8080
USER $APP_UID
ENTRYPOINT ["dotnet", "License.Api.dll"]
如果你的项目最终统一采用.NET 8 LTS,也可以把:
10.0
统一换成:
8.0
关键是:
整个项目统一Target Framework,不要API用一个大版本、Worker又用另一个版本。
九、Worker Dockerfile
FROM mcr.microsoft.com/dotnet/sdk:10.0 AS build
WORKDIR /src
COPY . .
RUN dotnet restore
RUN dotnet publish \
License.Worker/License.Worker.csproj \
-c Release \
-o /app/publish \
--no-restore
FROM mcr.microsoft.com/dotnet/runtime:10.0
WORKDIR /app
COPY --from=build \
/app/publish .
USER $APP_UID
ENTRYPOINT [
"dotnet",
"License.Worker.dll"
]
十、Admin Dockerfile
你之前确定:
Next.js
TypeScript
Bun
这里建议真正使用 Bun,而不是之前示例中的 Node。
FROM oven/bun:1 AS deps
WORKDIR /app
COPY package.json bun.lock ./
RUN bun install --frozen-lockfile
FROM oven/bun:1 AS build
WORKDIR /app
COPY --from=deps /app/node_modules ./node_modules
COPY . .
RUN bun run build
FROM oven/bun:1-alpine AS runtime
WORKDIR /app
ENV NODE_ENV=production
COPY --from=build /app/public ./public
COPY --from=build /app/.next ./.next
COPY --from=build /app/node_modules ./node_modules
COPY --from=build /app/package.json ./package.json
EXPOSE 3000
CMD ["bun", "run", "start"]
这样:
macOS
Windows
Linux
CI
Docker
都使用同一个:
bun.lock
减少依赖差异。
十一、Next.js环境变量
需要特别注意:
NEXT_PUBLIC_*
意味着:
这个变量最终可能进入浏览器。
因此:
NEXT_PUBLIC_API_URL=https://api.example.com
可以。
但是:
NEXT_PUBLIC_STRIPE_SECRET_KEY=...
绝对不可以。
Secret只能:
服务器端
使用。
十二、Nginx配置
例如:
api.example.com
配置:
server {
listen 443 ssl http2;
server_name api.example.com;
ssl_certificate
/etc/nginx/certs/fullchain.pem;
ssl_certificate_key
/etc/nginx/certs/privkey.pem;
location / {
proxy_pass
http://api:8080;
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;
}
}
十三、Admin域名
例如:
admin.example.com
Nginx:
server {
listen 443 ssl http2;
server_name admin.example.com;
location / {
proxy_pass
http://admin:3000;
proxy_set_header
Host $host;
proxy_set_header
X-Forwarded-For
$proxy_add_x_forwarded_for;
}
}
十四、不要让Admin直接访问数据库
错误架构:
Next.js
│
▼
PostgreSQL
正确:
Next.js
│
▼
ASP.NET Core API
│
▼
PostgreSQL
Admin是:
Frontend
而不是数据库管理程序。
十五、EF Core Migration
开发机器:
dotnet ef migrations add InitialCreate
生产:
不要每次:
dotnet ef database update
手动执行。
建议部署流程:
CI
↓
Build
↓
Migration Bundle
↓
审核
↓
Production
↓
Migration
↓
Deploy
十六、Migration Bundle
安装:
dotnet tool install \
--global dotnet-ef
生成:
dotnet ef migrations bundle \
--configuration Release \
-o ./efbundle
生产服务器执行:
./efbundle \
--connection "$DATABASE_CONNECTION"
十七、为什么不推荐API启动时自动Migration
很多项目写:
db.Database.Migrate();
生产环境并不推荐这样做。
因为:
API启动
↓
Migration
↓
Migration锁
↓
多个API实例
可能造成部署问题。
更好的:
CI/CD
↓
Migration Job
↓
完成
↓
部署API
十八、数据库备份
License系统:
数据库就是核心资产。
至少备份:
Users
Orders
Payments
Licenses
Activations
AuditLogs
十九、pg_dump
最简单:
pg_dump \
-h localhost \
-p 5432 \
-U license_app \
-F c \
license_platform \
> license_platform.dump
恢复:
pg_restore \
-h localhost \
-p 5432 \
-U license_app \
-d license_platform \
license_platform.dump
二十、自动备份脚本
文件:
scripts/backup-db.sh
#!/usr/bin/env bash
set -euo pipefail
BACKUP_DIR="/opt/license-platform/backups"
DATE=$(date +"%Y-%m-%d_%H-%M-%S")
mkdir -p "$BACKUP_DIR"
docker exec \
license-platform-postgres-1 \
pg_dump \
-U "$POSTGRES_USER" \
-d "$POSTGRES_DB" \
-F c \
> "$BACKUP_DIR/license_$DATE.dump"
find "$BACKUP_DIR" \
-type f \
-mtime +14 \
-delete
二十一、备份不是只存服务器
这是非常重要的一点。
错误:
服务器
└── backups/
如果:
硬盘损坏
服务器被删除
勒索软件
备份也可能一起丢失。
至少:
生产服务器
│
├── 本地备份
│
└── 异地备份
例如:
S3
Cloudflare R2
Backblaze B2
另一台服务器
二十二、3-2-1备份原则
推荐:
3 copies
2 different storage media
1 off-site
例如:
Primary PostgreSQL
│
├── Local backup
│
├── NAS backup
│
└── Cloud Object Storage
二十三、备份加密
数据库备份包含:
用户信息
订单
License
邮箱
因此:
Backup本身也必须受到保护。
例如:
gpg \
--symmetric \
license_2026.dump
生产环境更推荐对象存储的服务端加密,并配合严格IAM权限。
二十四、恢复测试
最容易犯的错误:
有备份 ≠ 能恢复。
至少每月进行:
下载备份
↓
创建临时PostgreSQL
↓
Restore
↓
检查License
↓
检查Order
↓
检查User
测试:
SELECT COUNT(*) FROM licenses;
SELECT COUNT(*) FROM orders;
SELECT COUNT(*) FROM users;
二十五、灾难恢复目标
建议定义:
RPO
允许最多丢失:
15分钟
如果商业规模较小:
1小时
也可以接受。
RTO
系统恢复目标:
1小时以内
对于刚开始运营的商业软件已经比较合理。
二十六、Redis备份策略
Redis主要用于:
Cache
Rate Limit
Session
Temporary Data
因此:
Redis不应该成为唯一数据源。
即使Redis全部丢失:
PostgreSQL
仍然应该能够恢复整个授权系统。
二十七、Redis故障
正确设计:
Redis挂掉
↓
API仍然可以运行
↓
部分缓存失效
↓
重新从PostgreSQL加载
而不是:
Redis挂掉
↓
License全部无法验证
二十八、Health Check
API:
GET /health
返回:
{
"status": "Healthy"
}
更完善:
/health/live
/health/ready
区别:
live
表示:
程序还活着。
ready
表示:
程序已经准备好接收流量。
二十九、Docker Healthcheck
API可以:
healthcheck:
test:
[
"CMD",
"wget",
"-q",
"-O",
"-",
"http://localhost:8080/health"
]
interval: 30s
timeout: 5s
retries: 3
三十、CI/CD架构
使用:
GitHub
↓
GitHub Actions
↓
Test
↓
Build
↓
Docker Image
↓
GHCR
↓
Production Server
↓
Deploy
三十一、GitHub Actions
文件:
.github/workflows/ci.yml
name: CI
on:
push:
branches:
- main
pull_request:
jobs:
backend:
runs-on: ubuntu-latest
steps:
- uses:
actions/checkout@v4
- uses:
actions/setup-dotnet@v4
with:
dotnet-version: '10.0.x'
- run:
dotnet restore
- run:
dotnet build
--configuration Release
--no-restore
- run:
dotnet test
--configuration Release
三十二、Admin CI
admin:
runs-on: ubuntu-latest
steps:
- uses:
actions/checkout@v4
- uses:
oven-sh/setup-bun@v2
with:
bun-version: latest
- run:
bun install --frozen-lockfile
- run:
bun run lint
- run:
bun run build
生产环境不建议简单使用:
bun-version: latest
更建议固定到经过测试的版本:
bun-version: 1.x.x
这样可以避免今天构建成功、下个月因为 Bun 新版本导致构建变化。
三十三、Docker Build
例如:
- name:
Build API image
run:
docker build
-t ghcr.io/company/license-api:${{ github.sha }}
./src/Backend
三十四、Push GHCR
- name:
Login GHCR
uses:
docker/login-action@v3
with:
registry: ghcr.io
username:
${{ github.actor }}
password:
${{ secrets.GITHUB_TOKEN }}
然后:
- name:
Push image
run:
docker push
ghcr.io/company/license-api:${{ github.sha }}
三十五、为什么不要只使用latest
不推荐:
license-api:latest
生产环境。
推荐:
license-api:
a82f91c
也可以:
license-api:
1.4.2
最好同时拥有:
semantic version
+
immutable commit SHA
例如:
license-api:1.4.2
license-api:a82f91c
三十六、生产部署
服务器:
cd /opt/license-platform/compose
拉取:
docker compose \
-f compose.prod.yml \
pull
执行Migration:
./scripts/migrate.sh
然后:
docker compose \
-f compose.prod.yml \
up -d
检查:
docker compose ps
三十七、部署顺序
生产部署推荐:
1. Backup Database
↓
2. Pull Images
↓
3. Run Migration
↓
4. Deploy API
↓
5. Deploy Worker
↓
6. Deploy Admin
↓
7. Nginx Reload
↓
8. Health Check
↓
9. Smoke Test
三十八、Smoke Test
上线后自动测试:
curl https://api.example.com/health
然后:
curl https://api.example.com/health/ready
再测试:
登录
创建订单
Sandbox支付
Webhook
License生成
License激活
Heartbeat
三十九、回滚策略
假设:
1.5.0
出现问题。
不要:
重新编译
直接回滚:
1.4.9
例如:
export VERSION=1.4.9
docker compose \
-f compose.prod.yml \
up -d
四十、数据库Migration回滚
这里需要特别小心。
应用可以:
1.5.0
↓
1.4.9
但是数据库:
Migration 20
↓
Migration 19
不一定可以安全回滚。
因此生产数据库Migration应该遵循:
Expand → Migrate → Contract
例如新增字段:
第一版本:
新增字段
第二版本:
开始写入
第三版本:
停止旧字段
第四版本:
删除旧字段
不要在一次部署中:
添加字段
+
删除旧字段
+
修改所有数据
这样回滚风险很高。
四十一、日志
API至少记录:
Request ID
User ID
License ID
Order ID
IP
HTTP Status
Latency
Exception
但是:
不要把License完整Key、密码、支付Secret写进日志。
例如:
错误:
LicenseKey=48D0-61CD-...
推荐:
LicenseId=8d8c...
或者:
LicenseHashPrefix=48D0...
四十二、结构化日志
ASP.NET Core建议:
Serilog
或者直接使用:
Microsoft.Extensions.Logging
输出:
{
"timestamp":
"2026-08-11T10:00:00Z",
"level":
"Information",
"event":
"LicenseActivation",
"licenseId":
"xxxx",
"deviceId":
"xxxx"
}
四十三、监控
第一阶段:
CPU
RAM
Disk
Container Status
HTTP Status
PostgreSQL
Redis
之后可以增加:
Prometheus
Grafana
监控:
API latency
5xx rate
License activation rate
Payment webhook failures
Worker failures
Database connections
四十四、磁盘监控
这个尤其重要。
如果:
/var/lib/docker
爆满:
可能导致:
PostgreSQL
Docker
Nginx
全部出现异常。
至少监控:
df -h
以及:
docker system df
不要在生产服务器上无脑执行:
docker system prune -a
因为可能删除你后续回滚需要的镜像。
四十五、最终生产架构
最终建议:
Cloudflare
│
▼
HTTPS
│
▼
Nginx
│
┌──────────────┴──────────────┐
│ │
▼ ▼
Admin Console ASP.NET API
│ │
│ ┌────────┼────────┐
│ │ │ │
│ ▼ ▼ ▼
│ PostgreSQL Redis Worker
│ │ │
│ │ │
│ └───────┬─────────┘
│ │
│ ▼
│ License Engine
│
▼
Download Center
▲
│
Payment Webhook
四十六、本阶段最终Checklist
Docker
☑ Multi-stage build
☑ Non-root container
☑ Healthcheck
☑ Restart policy
☑ Internal network
☑ Persistent volumes
PostgreSQL
☑ 不暴露5432
☑ 独立业务用户
☑ 自动备份
☑ 异地备份
☑ Restore测试
Redis
☑ 只允许内部访问
☑ AOF
☑ 不作为核心数据源
API
☑ HTTPS
☑ Health Check
☑ Rate Limit
☑ Structured Logging
☑ Production Config
CI/CD
☑ Automated Test
☑ Docker Build
☑ GHCR
☑ Immutable Image Tag
☑ Migration
☑ Deployment
☑ Rollback
Security
☑ Secrets不进Git
☑ DB不暴露公网
☑ Redis不暴露公网
☑ Admin MFA
☑ Webhook签名验证
☑ Backup加密
四十七、目前系统进度
现在已经完成:
┌────────────────────────────────────┐
│ License Commercial Platform │
├────────────────────────────────────┤
│ │
│ Backend API ✅ │
│ PostgreSQL ✅ │
│ EF Core ✅ │
│ License Engine ✅ │
│ Client SDK ✅ │
│ Admin Console ✅ │
│ Payment ✅ │
│ Auto Delivery ✅ │
│ Docker ✅ │
│ CI/CD ✅ │
│ Backup / Restore ✅ │
│ │
└────────────────────────────────────┘
接下来已经进入最后几个非常关键的阶段。
下一章:第十(九)——生产级安全加固与商业软件防破解
这一章会重点处理**“你的授权服务器安全”和“你的客户端软件安全”**两个完全不同的层面,包括:
服务器端:
WAF
Rate Limit
Anti-Bot
API Abuse Prevention
RBAC
MFA
Secret Management
Key Rotation
Audit
Webhook Security
Database Security
以及客户端:
License缓存保护
ECDSA验证
设备绑定
时间回拨检测
Replay Protection
代码签名
.NET程序集保护
混淆策略
完整性检测
Native Bridge
TPM / Secure Enclave
其中有一点我会特别调整前面第十(五)的示例:AES-256-GCM、本地密钥保护和设备指纹不能简单地用一个固定的跨平台AES Key实现。真正商业发行时,需要根据 Windows、macOS、Linux 分别利用操作系统的安全存储能力,否则攻击者拿到程序后很容易连加密Key一起拿走。
这一章完成后,再进入第十(十):商业发行、代码签名、Windows/macOS/Linux最终交付与上线Checklist,整个第十部分就基本闭环。
第十(九):生产级安全加固与商业软件防破解
这一章非常关键。前面的系统已经能够:
支付
↓
自动发货
↓
License
↓
客户端激活
↓
设备限制
现在要解决两个不同的问题:
- License Server 如何做到商业级安全
- 客户端如何提高破解成本
需要先明确一个原则:
客户端永远不是可信环境。
无论你使用 C#、C++、Rust 还是其他语言,只要代码运行在用户自己的电脑上,理论上都可能被分析、修改或调试。
因此真正可靠的商业授权系统不能依赖“客户端绝对无法破解”,而应该采用:
服务器可信
+
客户端多层验证
+
短期授权凭证
+
设备控制
+
异常检测
+
代码签名
+
完整性保护
+
风险控制
形成纵深防御。
一、最终安全架构
推荐最终结构:
Internet
│
▼
Cloudflare
│
WAF / Rate Limit
│
▼
Nginx
│
┌──────────────┴──────────────┐
│ │
▼ ▼
Admin Console License API
│
┌──────────────────────┼───────────────────┐
│ │ │
▼ ▼ ▼
PostgreSQL Redis Audit Log
│
▼
License Engine
│
▼
Risk Detection
客户端:
┌──────────────────────────────────────┐
│ Client App │
│ │
│ License Input │
│ ↓ │
│ Local Validation │
│ ↓ │
│ Device Identity │
│ ↓ │
│ Secure Local Storage │
│ ↓ │
│ Server Challenge │
│ ↓ │
│ Signature Verification │
│ ↓ │
│ Runtime Integrity │
│ ↓ │
│ Feature Authorization │
└──────────────────────────────────────┘
二、最重要的密码学架构调整
你最初的需求是:
激活码不能被内部人员随意查看。
这个需求不能简单通过:
AES 加密 LicenseKey
解决。
推荐采用:
License Key
│
▼
HMAC / SHA-256
│
▼
License Lookup Hash
│
▼
Database
数据库不保存明文License Key。
三、数据库不要保存完整License Key
不要:
LicenseKey
----------------------
48D0-61CD-35FE-...
改成:
LicenseId
KeyHash
Status
ProductId
MaxDevices
ExpiresAt
例如:
LicenseId:
01JXXXXXXX
KeyHash:
b8c2....
Status:
Active
MaxDevices:
3
四、为什么使用HMAC而不是简单SHA256
如果:
SHA256(LicenseKey)
攻击者拿到数据库以后,可以尝试:
候选License
↓
SHA256
↓
数据库比对
如果License格式比较规律,可能进行离线猜测。
更推荐:
HMAC-SHA256(
serverSecret,
licenseKey
)
即:
Hash =
HMACSHA256(
Secret,
NormalizedLicenseKey
)
这样数据库泄露后,攻击者无法仅凭数据库进行普通离线验证。
五、License Key格式
你之前设计:
48D0-61CD-35FE-9C9B-5358-3725-D644-1A18
可以继续使用。
总长度:
8 × 4 = 32
十六进制字符。
但是生产环境建议增加熵。
例如:
8组 × 4字符
共:
128 bit
已经足够作为随机License。
关键是:
必须使用密码学安全随机数生成器。
C#:
RandomNumberGenerator
不要:
Random
六、License生成器
using System.Security.Cryptography;
public static class LicenseKeyGenerator
{
public static string Generate()
{
Span<byte> bytes = stackalloc byte[16];
RandomNumberGenerator.Fill(bytes);
var hex =
Convert.ToHexString(bytes);
return string.Join(
"-",
Enumerable.Range(0, 8)
.Select(i =>
hex.Substring(i * 4, 4))
);
}
}
生成:
A82F-91C4-7B32-...
七、License Hash
using System.Security.Cryptography;
using System.Text;
public sealed class LicenseHasher
{
private readonly byte[] _secret;
public LicenseHasher(string secret)
{
_secret =
Convert.FromBase64String(secret);
}
public string Hash(string licenseKey)
{
var normalized =
licenseKey
.Replace("-", "")
.Trim()
.ToUpperInvariant();
using var hmac =
new HMACSHA256(_secret);
var hash =
hmac.ComputeHash(
Encoding.UTF8.GetBytes(
normalized));
return Convert.ToHexString(hash);
}
}
数据库只保存:
KeyHash
八、为什么后台管理员也不应该看到License明文
Admin Console:
错误:
License:
48D0-61CD-35FE-9C9B...
推荐:
License:
••••-••••-••••-9C9B
Status:
Active
Devices:
2 / 3
管理员只能看到:
License ID
Key后4位
用户
产品
状态
设备
创建时间
这样即使Admin账户被盗,风险也小很多。
九、用户找回License怎么办?
这也是为什么之前建议:
License Key
只在首次生成/购买后展示一次。
之后:
用户登录
↓
邮箱验证
↓
License Center
↓
查看授权状态
如果确实需要重新显示完整Key:
重新身份验证
+
Email OTP
+
短期Session
并记录:
LicenseReveal
审计日志。
十、管理员权限进一步细分
建议最终RBAC:
SuperAdmin
Admin
Support
Finance
SecurityAuditor
权限:
license.read
license.create
license.revoke
user.read
user.disable
payment.read
payment.refund
audit.read
security.manage
特别是:
license.reveal
应该成为一个极高权限。
甚至:
默认不存在“查看完整License”的权限。
十一、Admin MFA
管理员不能只使用:
Email
+
Password
必须:
Password
+
MFA
推荐:
TOTP
或者:
WebAuthn / Passkey
商业系统更推荐优先支持Passkey/WebAuthn。
十二、Admin登录安全
登录流程:
Email
↓
Password
↓
Rate Limit
↓
MFA
↓
Session
失败:
5次
↓
短暂锁定
但不要使用永久锁定,否则攻击者可以通过故意失败来阻止管理员登录。
十三、管理员Session
不要使用永久JWT:
JWT = 30 days
推荐:
Access Token
≈ 10~15分钟
Refresh Token
≈ 几天
Refresh Token必须:
Rotation
+
Revocation
+
Device Binding
十四、Refresh Token不要明文保存
数据库不要:
RefreshToken
----------------
eyJhbGciOi...
保存:
TokenHash
服务器收到Token后:
Token
↓
Hash
↓
Database
↓
Compare
十五、API Rate Limit
至少分三层:
IP
+
Account
+
Endpoint
例如:
/api/auth/login
限制:
10 requests / minute / IP
而:
/api/license/activate
可以更严格:
20 requests / minute / IP
具体数值应通过真实流量压测调整,不建议写死为一套“万能数字”。
十六、激活接口必须防爆破
攻击者可能:
AAAA-AAAA-AAAA...
BBBB-BBBB-...
CCCC-CCCC-...
因此:
POST /api/license/activate
必须:
Rate Limit
+
Request Signature
+
Device Challenge
+
Risk Scoring
十七、不要返回过多错误信息
错误:
{
"error": "License exists but belongs to another user"
}
这会帮助攻击者枚举License。
推荐统一:
{
"error": "Activation failed"
}
服务器内部日志记录真实原因。
十八、设备ID设计
你需要支持:
1台
3台
5台
因此数据库:
License
│
├── Activation Device A
├── Activation Device B
└── Activation Device C
设备记录:
DeviceId
LicenseId
Platform
AppVersion
FirstSeen
LastSeen
Status
十九、不要把MAC地址当设备ID
不要:
MAC Address
作为唯一设备身份。
原因:
- 可以修改
- 虚拟机容易变化
- 多网卡
- Docker
- 隐私问题
- macOS/Linux/Windows行为不同
推荐:
随机Device Identity
+
OS Secure Storage
+
服务器记录
二十、客户端生成Device Identity
首次启动:
Random 256-bit ID
然后存储到:
Windows:
Credential Manager / DPAPI
macOS:
Keychain
Linux:
Secret Service
如果系统没有可用安全存储:
不要假装它是安全的。
可以降级为普通本地存储,但服务器风险评分提高。
二十一、Windows安全存储
C#:
DPAPI
例如:
ProtectedData.Protect(...)
密钥绑定:
CurrentUser
或者更高安全级别的:
LocalMachine
具体选择取决于你的应用部署模型。
二十二、macOS
推荐:
Keychain Services
不要:
~/.config/app/license.json
里面直接保存:
License
DeviceSecret
RefreshToken
二十三、Linux
优先:
Secret Service
例如:
GNOME Keyring
KWallet
服务器端也必须允许:
Linux Headless
这种场景使用不同策略。
二十四、License Token
客户端激活成功后:
不要每次:
LicenseKey
直接发送。
推荐服务器签发:
License Access Token
例如:
Activation
↓
License Key
↓
Server
↓
验证
↓
Signed License Token
客户端以后使用:
License Token
而不是反复发送原始License Key。
二十五、签名License Token
建议:
Ed25519
或者:
ECDSA P-256
架构:
Server
Private Key
│
▼
Sign License Token
│
▼
Client
Public Key
│
▼
Verify Signature
关键:
私钥永远不能进入客户端。
二十六、为什么非常适合使用非对称签名
客户端里面可以包含:
PUBLIC KEY
即使被提取:
攻击者拿到Public Key
也无法生成:
有效License Token
因为:
Private Key
只存在服务器。
这是整个客户端授权安全的核心之一。
二十七、密钥轮换
不要永久只有:
license-key-v1
推荐:
kid = license-2026-01
Token:
{
"kid": "license-2026-01",
"alg": "EdDSA"
}
服务器以后可以:
license-2026-01
license-2027-01
并保留旧公钥验证历史License。
二十八、客户端验证流程
最终:
License Key
↓
Activate
↓
Server
↓
Device Check
↓
License Check
↓
Issue Signed Token
↓
Client
↓
Signature Verify
↓
Secure Storage
启动:
读取Token
↓
验证签名
↓
检查Expiration
↓
检查Device
↓
运行
二十九、在线Heartbeat
不要让客户端:
激活一次
↓
永远离线使用
推荐:
首次激活
↓
在线验证
↓
Token
↓
周期性Heartbeat
例如:
24小时
或者根据产品类型设计更长/更短的验证周期。
三十、离线模式
商业软件通常需要考虑:
飞机
企业内网
没有网络
因此:
Online License
+
Offline Grace Period
例如:
服务器最后验证
↓
允许短期离线运行
↓
超过Grace Period
↓
需要重新验证
具体期限应根据你的商业产品场景决定。
三十一、时间回拨
攻击者可能:
系统时间
2026-08-11
↓
修改成
2025-01-01
客户端不要只相信:
DateTime.Now
可以结合:
LastSuccessfulServerTime
例如:
ServerTime = 2026-08-11
LocalTime = 2025-01-01
检测:
ClockRollback
然后:
重新在线验证
三十二、Replay Protection
每次客户端请求可以包含:
RequestId
Timestamp
Nonce
例如:
{
"deviceId": "...",
"timestamp": 1786440000,
"nonce": "...",
"requestId": "..."
}
服务器检查:
Timestamp
Nonce
RequestId
防止攻击者复制以前的合法请求。
三十三、客户端不要把授权逻辑全部集中在一个函数
错误:
if (licenseValid)
{
StartApplication();
}
攻击者只需要Patch:
licenseValid = true;
因此需要:
License
↓
Feature
↓
Session
↓
Runtime
多处验证。
三十四、但不要做“疯狂反调试”
不要把客户端设计成:
发现Debugger
↓
立即删除文件
或者:
检测虚拟机
↓
破坏用户数据
这种做法不仅不可靠,也容易误伤合法用户。
推荐:
检测
↓
提高风险评分
↓
要求重新在线验证
而不是破坏用户环境。
三十五、.NET程序保护
你的客户端如果使用:
C#
.NET
需要认识到:
.NET程序集天然比Native代码更容易被反编译。
因此商业发行可以使用成熟的:
.NET Obfuscator
对关键程序集进行:
- 名称混淆
- 控制流混淆
- 字符串保护
- 常量保护
- Anti-Tamper
但不要试图把整个程序都做成极端混淆。
三十六、关键逻辑Native化
如果授权系统的部分逻辑特别敏感,可以考虑:
C#
Application
│
▼
Native Library
│
▼
C++ / Rust
例如:
Device Secret Derivation
Cryptographic Operations
Integrity Checks
但要注意:
Native代码也不是不可破解,只是提高分析成本。
三十七、不要把服务器Secret编译进客户端
这是绝对禁止的:
const string Secret =
"SUPER_SECRET_123456";
因为:
客户端
↓
反编译
↓
Secret
直接泄露。
客户端最多包含:
Public Key
不能包含:
Private Key
HMAC Master Secret
Database Password
Stripe Secret
Webhook Secret
三十八、客户端完整性
可以对关键程序集计算:
SHA-256
例如:
LicenseClient.dll
启动时:
Hash
↓
Server
↓
Expected Hash
但是这种方案本身也容易被Patch。
所以更推荐:
代码签名
+
完整性检查
+
服务器风险检测
组合使用。
三十九、Windows代码签名
Windows商业发行:
Authenticode
签名:
.exe
.dll
.msi
目标:
Publisher:
Your Company
用户看到:
Verified Publisher
四十、macOS代码签名
macOS商业发行需要:
Apple Developer ID
通常流程:
Build
↓
Code Sign
↓
Hardened Runtime
↓
Notarization
↓
Staple
↓
DMG / PKG
这样用户安装时可信度会高很多。
四十一、Linux
Linux没有完全统一的商业代码签名体验。
建议:
.deb
.rpm
AppImage
同时:
SHA256
+
GPG签名
例如:
app.AppImage
app.AppImage.sha256
app.AppImage.sig
四十二、下载文件安全
下载服务器:
https://download.example.com
不要让API每次:
读取整个安装包
推荐:
Object Storage
│
▼
Signed URL
│
▼
Client
例如:
下载链接有效期
10分钟
这样即使链接泄露,也不会永久有效。
四十三、软件更新
客户端:
Current Version
↓
Update API
↓
Latest Version
↓
Download
↓
Signature Verification
↓
Install
不要只检查:
SHA256
因为Hash只能证明:
文件没有被意外修改。
数字签名才能证明:
文件由你的发行密钥签署。
四十四、更新Manifest
例如:
{
"version": "1.4.2",
"platform": "windows-x64",
"url": "...",
"sha256": "...",
"signature": "...",
"minimumClient": "1.2.0"
}
这个Manifest本身也应该进行签名。
四十五、服务器风控
服务器可以建立:
Risk Score
例如:
正常设备
= 0
短时间大量激活
+30
IP异常
+20
设备数量异常
+30
大量失败License
+20
Token Replay
+50
超过阈值:
Risk > 70
要求:
重新验证
而不是直接封号。
四十六、异常激活示例
例如一个License:
最大设备:
3
正常:
China
PC1
China
PC2
China
PC3
可疑:
PC1
PC2
PC3
PC4
PC5
PC6
PC7
服务器:
DeviceLimitExceeded
四十七、不要只根据IP限制设备
不要:
1 IP = 1 Device
因为:
公司
学校
家庭
VPN
NAT
可能大量用户共享IP。
IP应该只是:
Risk Signal
而不是唯一判断依据。
四十八、审计日志
以下事件必须记录:
Login
LoginFailed
LicenseCreated
LicenseActivated
LicenseDeactivated
LicenseRevoked
DeviceAdded
DeviceRemoved
PaymentSucceeded
PaymentRefunded
WebhookFailed
AdminAction
SecurityEvent
例如:
{
"event": "LicenseActivated",
"licenseId": "...",
"deviceId": "...",
"ip": "...",
"timestamp": "..."
}
四十九、日志中禁止出现的内容
绝对不要记录:
Password
RefreshToken
Payment Secret
Database Password
Private Key
完整LicenseKey
例如错误:
_logger.LogInformation(
"License={License}",
licenseKey);
应该改成:
_logger.LogInformation(
"License activated: {LicenseId}",
licenseId);
五十、数据库加密
这里需要区分:
磁盘加密
防止:
硬盘被盗
应用层加密
保护:
数据库泄露
对于特别敏感字段:
CustomerEmail
Payment metadata
可以根据合规需求进行字段级加密。
但是:
License Key最好直接设计为不可逆存储,而不是“加密后还能被管理员解密”。
五十一、Secret Management
生产环境:
Source Code
│
├── ❌ Password
├── ❌ Stripe Secret
├── ❌ DB Password
└── ❌ Private Key
应该:
Secret Manager
│
▼
Application
例如:
AWS Secrets Manager
Azure Key Vault
GCP Secret Manager
HashiCorp Vault
五十二、Private Key保护
License签名私钥是整个系统最重要的资产之一。
不要:
license-private.pem
直接放:
Docker Image
也不要:
Git Repository
生产环境推荐:
KMS / HSM
最终架构:
License Server
│
▼
KMS / HSM
│
▼
Sign()
私钥本身不会直接暴露给应用。
五十三、开发环境和生产环境必须分离
必须:
Development
Staging
Production
三套环境。
尤其:
License Signing Key
Payment Secret
Database
不能共用。
五十四、测试License
开发环境:
TEST-XXXX-XXXX
生产:
LIVE-XXXX...
服务器必须区分:
Environment
防止开发人员拿生产License进行测试。
五十五、Staging环境
推荐:
api-staging.example.com
admin-staging.example.com
部署:
GitHub
↓
CI
↓
Staging
↓
自动测试
↓
人工确认
↓
Production
五十六、生产发布审批
商业系统不要:
git push
↓
直接生产
推荐:
Pull Request
↓
CI
↓
Code Review
↓
Security Test
↓
Staging
↓
Approval
↓
Production
五十七、供应链安全
需要固定:
NuGet versions
npm/Bun dependencies
Docker base images
GitHub Actions
不要:
latest
到处乱用。
例如:
postgres:17
长期运行后也应该规划升级策略。
更严格:
postgres:17.x
甚至固定到经过验证的具体版本。
五十八、依赖漏洞扫描
CI加入:
NuGet Audit
npm audit
Trivy
GitHub Dependabot
例如:
Source
↓
Dependency Scan
↓
Docker Scan
↓
SAST
↓
Build
五十九、Docker镜像扫描
生产镜像构建后:
Trivy
检查:
OS vulnerabilities
Library vulnerabilities
Secrets
Misconfigurations
发现严重漏洞:
CI失败
六十、WAF
生产:
Cloudflare
↓
WAF
↓
Nginx
↓
ASP.NET
WAF可以处理:
恶意Bot
扫描
异常请求
DDoS
部分Web攻击
但是:
WAF不是应用安全的替代品。
API自身仍然必须:
Authentication
Authorization
Validation
Rate Limit
六十一、API输入验证
所有输入:
LicenseKey
Email
DeviceId
ProductId
OrderId
都必须验证。
推荐:
FluentValidation
或者:
DataAnnotations
不要相信客户端:
{
"maxDevices": 999999
}
服务器应该:
Plan
↓
Server-side configuration
↓
MaxDevices
六十二、IDOR防护
例如:
GET /api/licenses/123
攻击者修改:
123 → 124
不能因此看到别人的License。
服务器必须:
CurrentUser
+
ResourceOwner
进行授权检查。
六十三、Admin API和普通API分离
推荐:
/api/v1/license/*
普通用户:
/api/v1/account/*
管理员:
/api/v1/admin/*
并且:
Admin Policy
单独控制。
六十四、API版本控制
不要:
/api/license
永久不变。
建议:
/api/v1/license
以后:
/api/v2/license
这样客户端SDK升级更加安全。
六十五、最终客户端防护层级
建议:
Layer 1
代码签名
↓
Layer 2
程序集保护
↓
Layer 3
License Token签名
↓
Layer 4
Device Identity
↓
Layer 5
Secure Storage
↓
Layer 6
Heartbeat
↓
Layer 7
Replay Protection
↓
Layer 8
Risk Detection
↓
Layer 9
Server-side Authorization
这样即使攻击者绕过其中一层:
仍然无法直接获得完整授权能力。
六十六、不要追求100%防破解
这是商业软件设计中非常重要的认识。
不存在:
100%不可破解
真正的目标应该是:
攻击成本
>
License购买成本
例如:
购买:
$49
破解需要:
数小时/数天逆向
大多数普通用户就不会选择破解。
六十七、真正应该保护的东西
不是:
if license == true
而是:
License Authority
也就是:
谁拥有授权?
授权什么产品?
允许多少设备?
有效期多久?
当前是否被撤销?
这些最终必须由服务器控制。
六十八、最终安全模型
整个系统最终形成:
┌───────────────────┐
│ License Server │
│ │
│ PostgreSQL │
│ License Engine │
│ KMS/HSM │
│ Risk Engine │
└─────────┬─────────┘
│
Signed Token
│
▼
┌───────────────────┐
│ Client │
│ │
│ Public Key │
│ Device Identity │
│ Secure Storage │
│ Integrity Check │
└───────────────────┘
最重要的一条:
Private Key
↓
只在服务器/KMS
客户端:
只有Public Key
六十九、本章节完成Checklist
Server
☑ License Key不可逆存储
☑ HMAC-SHA256
☑ RBAC
☑ Admin MFA
☑ API Rate Limit
☑ Webhook验证
☑ Replay Protection
☑ Audit Log
☑ Secret Management
☑ KMS/HSM规划
☑ WAF
☑ Dependency Scan
Client
☑ Device Identity
☑ Secure Storage
☑ Signed License Token
☑ Token Expiration
☑ Heartbeat
☑ Offline Grace Period
☑ Clock Rollback Detection
☑ Code Signing
☑ Integrity Check
☑ .NET Obfuscation
Infrastructure
☑ HTTPS
☑ DB不暴露公网
☑ Redis不暴露公网
☑ Docker Non-root
☑ Backup Encryption
☑ Off-site Backup
☑ Restore Test
☑ CI/CD
☑ Staging
☑ Production
七十、现在整个第十部分的进度
第十(一) 工程初始化 ✅
第十(二) ASP.NET Core Backend ✅
第十(三) PostgreSQL + EF Core ✅
第十(四) License Server ✅
第十(五) Client SDK ✅
第十(六) Admin Console ✅
第十(七) Payment + 自动发货 ✅
第十(八) Production + Docker + CI/CD ✅
第十(九) Security + Anti-Crack ✅
第十(十) 商业发行与最终交付 ⏳
因此现在只剩下**第十(十)**这个主要章节。
下一章将把真正面向客户的最终发行体系补齐:
Windows
├── EXE
├── MSI
└── Code Signing
macOS
├── Universal / x64 / ARM64
├── DMG
├── Code Signing
└── Notarization
Linux
├── AppImage
├── DEB
└── RPM
↓
Auto Updater
↓
Download Center
↓
License Activation
↓
Crash Reporting
↓
Telemetry
↓
Support
并最终给出一份可以真正用于上线前逐项执行的:
《License Platform 商业上线 Checklist》,把服务器、数据库、支付、License、客户端、域名、HTTPS、代码签名、备份、监控、安全、隐私政策和发布流程全部串起来。
第十(十):商业发行、自动更新与最终上线 Checklist
这一章把前面的系统真正收口到可以商业运营的状态。
最终目标不是单纯“能激活”,而是形成:
┌──────────────┐
│ 用户购买 │
└──────┬───────┘
↓
Payment Provider
↓
Payment Webhook
↓
Order Verification
↓
License Generator
↓
自动发货 Email
↓
┌──────────────────┴─────────────────┐
↓ ↓
Download Center License Center
↓ ↓
安装软件 管理授权
↓ ↓
Client SDK Activate Device
↓ ↓
License Server ←──────────────→ Device Limit
↓
Signed License Token
↓
Software Runtime
一、最终项目结构
建议最终采用 Monorepo:
license-platform/
│
├── apps/
│ │
│ ├── license-server/
│ │ └── License.Server/
│ │
│ ├── admin-console/
│ │
│ ├── customer-portal/
│ │
│ ├── download-server/
│ │
│ └── docs/
│
├── sdk/
│ │
│ ├── dotnet/
│ │ ├── License.Client/
│ │ └── License.Client.Abstractions/
│ │
│ ├── native/
│ │
│ └── protocol/
│
├── clients/
│ │
│ ├── windows/
│ │
│ ├── macos/
│ │
│ └── linux/
│
├── packages/
│ │
│ ├── shared-contracts/
│ ├── crypto/
│ └── testing/
│
├── infrastructure/
│ │
│ ├── docker/
│ ├── nginx/
│ ├── postgres/
│ ├── redis/
│ └── monitoring/
│
├── deploy/
│ │
│ ├── staging/
│ └── production/
│
├── tests/
│ │
│ ├── unit/
│ ├── integration/
│ ├── security/
│ └── load/
│
├── scripts/
│
├── .github/
│ └── workflows/
│
├── Directory.Build.props
├── Directory.Packages.props
├── global.json
├── docker-compose.yml
└── README.md
二、为什么推荐Monorepo
你的项目本质上存在大量共享模型:
License
Product
Plan
Device
Activation
Order
Customer
如果拆成很多Repository:
license-server
client-sdk
admin
customer-portal
desktop-client
很容易出现:
Server:
LicenseStatus.Active
Client:
LicenseStatus.Enabled
最终协议不一致。
Monorepo可以让:
API Contract
SDK
Server
Tests
一起演进。
三、最终技术栈
结合你之前希望:
macOS + Windows + Linux开发 C# ASP.NET Core PostgreSQL Bun TypeScript
我建议最终版本:
| 模块 | 技术 |
|---|---|
| License Server | ASP.NET Core |
| Language | C# |
| ORM | EF Core |
| Database | PostgreSQL |
| Cache | Redis |
| Admin | Next.js + TypeScript |
| Customer Portal | Next.js + TypeScript |
| Package Manager | Bun |
| Client SDK | .NET |
| Windows Client | .NET |
| macOS Client | .NET / Native |
| Linux Client | .NET |
| Container | Docker |
| Reverse Proxy | Nginx / Caddy |
| CI/CD | GitHub Actions |
| Monitoring | OpenTelemetry |
| Logs | Loki / Seq / ELK |
| Metrics | Prometheus |
| Dashboard | Grafana |
四、TypeScript版本建议
你之前问过:
TypeScript 7.0 能不能使用?
如果你的开发环境已经确认 TypeScript 7.x 与项目依赖兼容,可以使用。
但是商业项目不要因为:
最新版本
就直接升级生产。
推荐原则:
Latest
↓
Compatibility Test
↓
CI
↓
Staging
↓
Production
而不是:
npm/Bun update
↓
生产服务器
尤其:
Next.js
React
TypeScript
ESLint
这些基础设施之间需要验证兼容性。
五、Bun是否可以替代Node.js
可以。
对于:
Admin Console
Customer Portal
Website
Tooling
Bun非常适合。
但是有一个原则:
生产运行时和开发包管理器是两个概念。
你可以:
Bun
↓
install
↓
lockfile
↓
build
↓
Next.js production
但不要让整个License Server依赖Node生态。
因为你的核心后端:
ASP.NET Core
完全独立。
最终:
C#
↓
License Authority
TypeScript/Bun
↓
Web Interface
是很合理的架构。
六、Node.js兼容性
即使你使用Bun,也建议保证:
Node.js compatibility
原因是某些第三方工具:
CLI
CI Action
Build Tool
Security Scanner
仍然假设Node存在。
所以开发电脑可以:
Bun
+
mise
同时保留一个LTS Node作为兼容工具链。
七、数据库最终推荐
生产:
PostgreSQL
开发:
PostgreSQL
测试:
PostgreSQL
不要:
开发:
SQLite
生产:
PostgreSQL
因为容易出现:
SQL差异
Transaction差异
Index差异
Concurrency差异
推荐统一:
Docker PostgreSQL
八、数据库版本策略
不要追求:
最新一天发布的版本
推荐:
PostgreSQL
当前主版本
+
经过验证的稳定小版本
例如:
PostgreSQL 17.x
然后通过:
Minor Upgrade
持续维护。
不要:
postgres:latest
作为生产数据库。
九、Docker最终原则
开发:
postgres:
image: postgres:17
生产:
固定到经过测试的具体17.x版本
例如:
postgres:17.x
真正上线前记录:
Docker Image Digest
这样可以保证:
今天部署
=
下个月重新部署
尽可能使用同一个镜像。
十、生产Docker结构
推荐:
Internet
│
▼
Cloudflare
│
▼
Nginx
│
├── License API
├── Admin
└── Customer Portal
│
▼
PostgreSQL
│
Redis
数据库:
❌ 不开放公网
Redis:
❌ 不开放公网
只有:
Nginx
License Server
Admin Server
Worker
需要对外开放。
十一、生产域名
推荐:
example.com
拆分:
www.example.com
app.example.com
api.example.com
admin.example.com
download.example.com
status.example.com
docs.example.com
其中:
api.example.com
专门用于License API。
十二、下载中心
建议:
download.example.com
提供:
Windows
macOS
Linux
例如:
Windows x64
Windows ARM64
macOS Apple Silicon
macOS Intel
Linux x64
Linux ARM64
不要让用户自己寻找:
GitHub Release
商业产品应该有自己的Download Center。
十三、版本Manifest
服务器维护:
{
"product": "MyApp",
"version": "2.5.0",
"channel": "stable",
"platform": "windows-x64",
"download": "...",
"sha256": "...",
"signature": "...",
"minimumOsVersion": "...",
"releaseDate": "..."
}
客户端请求:
GET /api/v1/releases/latest
服务器返回:
Latest Version
Download URL
SHA256
Signature
十四、Release Channel
商业软件建议:
stable
beta
nightly
默认:
stable
开发人员:
beta
内部测试:
nightly
这样不会因为:
最新测试版本
导致所有用户一起升级。
十五、自动更新
完整流程:
Application
│
▼
Check Update
│
▼
Manifest
│
├── No Update
│
└── Update Available
│
▼
Download
│
▼
Verify Signature
│
▼
Verify Hash
│
▼
Install
│
▼
Restart
十六、自动更新最重要的安全原则
不要:
Download
↓
直接执行
必须:
Download
↓
SHA256
↓
Digital Signature
↓
Trusted Publisher
↓
Install
十七、Windows发行
最终:
MyAppSetup.exe
或者:
MyApp.msi
签名:
Authenticode
最终检查:
☑ EXE签名
☑ DLL签名
☑ Installer签名
☑ Publisher正确
十八、macOS发行
推荐:
MyApp.dmg
支持:
Apple Silicon
Intel
如果客户端是.NET:
Universal
或者分别:
osx-arm64
osx-x64
最终:
Code Sign
↓
Hardened Runtime
↓
Notarization
↓
DMG
十九、Linux发行
建议同时:
AppImage
.deb
.rpm
因为:
Ubuntu/Debian
和:
Fedora/RHEL
用户需求不同。
AppImage作为:
Portable
版本。
二十、客户端首次启动
最终体验建议:
┌─────────────────────────────┐
│ MyApp │
│ │
│ Welcome │
│ │
│ License Key │
│ ┌───────────────────────┐ │
│ │ XXXX-XXXX-XXXX-XXXX │ │
│ └───────────────────────┘ │
│ │
│ [ Activate ] │
│ │
│ Already activated? │
│ Sign in │
└─────────────────────────────┘
二十一、激活成功
服务器:
License
↓
Customer
↓
Device
↓
Activation
↓
Signed Token
客户端:
Activation successful
显示:
Plan:
Professional
Devices:
2 / 3
Expires:
2027-08-11
不要显示:
服务器内部ID
风险评分
HMAC
Token
二十二、设备管理
Customer Portal:
My Licenses
Professional
3 Devices
────────────────────
Windows PC
Activated:
2026-08-01
macOS
Activated:
2026-08-04
Linux
Activated:
2026-08-08
用户可以:
Deactivate
释放:
1 Device
二十三、防止“无限释放设备”
否则:
激活
↓
释放
↓
另一台
↓
释放
↓
另一台
就失去设备限制意义。
可以加入:
Device Change Policy
例如:
30 days
允许更换N次
或者:
正常用户:
自由管理
异常大量更换:
需要重新验证
不要仅凭IP粗暴封禁。
二十四、退款自动处理
支付:
Payment
↓
Order
↓
License
退款:
Refund
↓
Webhook
↓
Order Refunded
↓
License
↓
Suspended
这样不会出现:
退款成功
+
License仍然永久有效
二十五、订阅产品
如果以后支持订阅:
Monthly
Yearly
Lifetime
License模型不要写死。
推荐:
License
↓
Plan
↓
Entitlement
例如:
Professional
├── Feature A
├── Feature B
├── 3 Devices
└── Cloud Sync
二十六、Feature Entitlement
这是未来扩展非常重要的一层。
不要:
if (license.Plan == "Pro")
到处写。
应该:
License
↓
Entitlements
↓
Feature
例如:
{
"cloudSync": true,
"exportPdf": true,
"maxDevices": 3
}
这样以后增加:
Enterprise
Team
Ultimate
不需要大量修改客户端代码。
二十七、客服体系
商业软件一定需要:
Support
用户可能遇到:
License无法激活
设备达到上限
换电脑
退款
邮箱变更
订单找不到
Customer Portal应该支持:
Ticket
至少记录:
Order ID
License ID
Customer ID
Issue
Status
CreatedAt
二十八、状态页面
建议:
status.example.com
显示:
License API Operational
Activation Operational
Payment Operational
Download Operational
Customer Portal Operational
发生故障时:
Investigating
Identified
Monitoring
Resolved
这对商业产品非常有价值。
二十九、监控
生产至少监控:
CPU
Memory
Disk
Network
Database
Redis
API latency
HTTP 5xx
Activation failure
Payment failure
Webhook failure
重点指标:
License activation success rate
例如:
正常:
99.8%
异常:
85%
应该触发告警。
三十、OpenTelemetry
ASP.NET Core建议统一:
OpenTelemetry
采集:
Traces
Metrics
Logs
例如:
POST /api/v1/license/activate
│
├── API
├── License Engine
├── PostgreSQL
└── Redis
可以快速定位:
到底慢在哪。
三十一、备份策略
数据库:
Daily Full Backup
+
WAL / PITR
至少:
3-2-1 Backup
即:
3 copies
2 different storage
1 off-site
三十二、备份加密
不要:
database.sql
裸放:
/home/backups
推荐:
Encrypt
↓
Object Storage
并限制:
Backup Access
三十三、恢复测试
最重要的一点:
有备份 ≠ 能恢复。
至少定期:
Create Test Server
↓
Restore Database
↓
Run Migration
↓
Start API
↓
Test Login
↓
Test Activation
如果恢复不了:
备份实际上没有价值。
三十四、灾难恢复
需要明确:
RPO
RTO
例如:
RPO = 15 min
RTO = 1 hour
意思:
最多丢15分钟数据
1小时内恢复服务
具体目标根据业务规模确定。
三十五、CI/CD最终流程
Developer
│
▼
Git Push
│
▼
Pull Request
│
▼
──────────────
CI
──────────────
│
├── Build
├── Unit Test
├── Integration Test
├── SAST
├── Dependency Scan
├── Docker Scan
└── License Test
│
▼
Staging
│
▼
Approval
│
▼
Production
三十六、Release Tag
正式发布:
v1.0.0
而不是:
latest
建议采用:
Semantic Versioning
例如:
1.0.0
1.0.1
1.1.0
2.0.0
三十七、数据库Migration不要自动乱跑
生产:
Application Start
↓
自动Migration
对于重要商业系统不建议无条件这么做。
推荐:
Build
↓
Migration Artifact
↓
Backup
↓
Migration
↓
Deploy Application
这样可以控制数据库变更。
三十八、上线前必须做压力测试
模拟:
100
500
1000
5000
并发激活请求。
重点观察:
CPU
DB connections
Redis
API latency
Lock contention
License uniqueness
尤其测试:
同一个License
100个请求同时激活
最终必须保证:
MaxDevices = 3
不会因为并发导致:
3 → 5
三十九、License并发测试
这是License Server最重要的测试之一。
例如:
License:
MAX_DEVICES = 3
同时发送:
Device A
Device B
Device C
Device D
Device E
数据库最终只能:
A
B
C
而:
D
E
必须失败。
必须使用:
Database Transaction
+
Unique Constraint
+
正确的并发控制
不能仅依赖C#:
if (devices.Count < max)
因为两个请求可能同时读取:
2 devices
然后都认为:
2 < 3
最终变成4台。
四十、最终数据库安全约束
数据库层必须保护业务规则。
例如:
LicenseDevice
建立唯一约束:
LicenseId
+
DeviceId
避免:
同一个设备
重复激活
同时:
License
必须有:
Unique(KeyHash)
保证License不会重复。
四十一、商业上线Checklist
A. 域名
☐ 主域名
☐ API域名
☐ Admin域名
☐ Download域名
☐ Status域名
☐ Docs域名
B. HTTPS
☐ HTTPS
☐ HTTP → HTTPS
☐ TLS配置
☐ HSTS
☐ Certificate Monitoring
C. Server
☐ Production Server
☐ Firewall
☐ SSH安全
☐ Non-root
☐ Docker
☐ Nginx
☐ Cloudflare/WAF
D. Database
☐ PostgreSQL
☐ Private Network
☐ Backup
☐ PITR
☐ Restore Test
☐ Monitoring
E. Redis
☐ Private Network
☐ Authentication
☐ Persistence策略
☐ Memory Limit
F. License
☐ HMAC KeyHash
☐ Device Limit
☐ Device Management
☐ Signed Token
☐ Ed25519/ECDSA
☐ Key Rotation
☐ Revocation
☐ Heartbeat
☐ Offline Grace
G. Security
☐ Admin MFA
☐ RBAC
☐ Rate Limit
☐ WAF
☐ Audit Log
☐ Secret Manager
☐ KMS/HSM
☐ Dependency Scan
☐ Container Scan
H. Client
☐ Windows
☐ macOS Intel
☐ macOS Apple Silicon
☐ Linux
☐ Secure Storage
☐ Code Signing
☐ Auto Update
☐ Signature Verification
☐ Crash Reporting
I. Payment
☐ Payment Provider
☐ Webhook
☐ Webhook Signature
☐ Idempotency
☐ Order
☐ Refund
☐ License Creation
☐ Automatic Delivery
J. Monitoring
☐ Logs
☐ Metrics
☐ Traces
☐ Alerting
☐ Status Page
☐ Error Tracking
四十二、正式上线前的安全测试
至少进行:
Authentication Test
Authorization Test
IDOR Test
Rate Limit Test
Replay Test
Brute Force Test
SQL Injection Test
XSS Test
CSRF Test
SSRF Test
Webhook Replay Test
License Race Test
Device Limit Test
Token Forgery Test
Expired Token Test
Clock Rollback Test
特别是:
Token Forgery
必须测试:
修改payload
修改expiration
修改licenseId
修改deviceId
修改plan
任何修改都必须:
Signature Invalid
四十三、最终安全边界
最终应该做到:
攻击者获得客户端
↓
可以分析客户端
↓
可以看到Public Key
↓
可以看到API Endpoint
↓
可以修改本地程序
但是:
无法伪造服务器签名
无法修改服务器License
无法突破服务器设备限制
无法直接读取数据库License Key
无法伪造支付Webhook
无法获得服务器Private Key
这才是真正的商业License系统。
四十四、你这个项目最终可以形成的产品
最终不仅仅是一个:
License激活程序
而是一套完整的:
Commercial Licensing Platform
包括:
┌─────────────────────────────────────┐
│ Commercial Platform │
├─────────────────────────────────────┤
│ │
│ Customer Portal │
│ Admin Console │
│ License Server │
│ Payment System │
│ Automatic Delivery │
│ Device Management │
│ Client SDK │
│ Update Server │
│ Download Center │
│ Security Engine │
│ Analytics │
│ Audit System │
│ Monitoring │
│ │
└─────────────────────────────────────┘
以后甚至可以把它做成:
SaaS License Platform
让其他软件开发者购买你的License服务。
四十五、整个第十部分至此完成
现在完整路线已经从:
第十(一)
项目初始化
↓
第十(二)
ASP.NET Core
↓
第十(三)
PostgreSQL + EF Core
↓
第十(四)
License Server
↓
第十(五)
Client SDK
↓
第十(六)
Admin Console
↓
第十(七)
Payment + 自动发货
↓
第十(八)
Docker + CI/CD + Backup
↓
第十(九)
Security + Anti-Crack
↓
第十(十)
Commercial Release
第十部分完整结束。
不过,如果你的目标不是“文档完成”,而是真的把这个项目开发出来,下一阶段就不应该继续无限增加章节,而应该进入真正的工程实现阶段。
我建议下一步直接进入:
第十一部分:从零创建真实可运行的完整项目
按照真实开发顺序逐文件实现:
01. Git Repository
02. .NET Solution
03. ASP.NET Core API
04. PostgreSQL
05. EF Core
06. Redis
07. Identity / Admin Auth
08. License Domain
09. License Engine
10. Device Activation
11. Signed Token
12. Client SDK
13. Admin Console
14. Customer Portal
15. Payment
16. Webhook
17. Email Delivery
18. Docker
19. CI/CD
20. Windows/macOS/Linux Client
21. Code Signing
22. Auto Update
23. Integration Tests
24. Security Tests
25. Production Deployment
而且这一阶段我建议不再用伪代码,直接按照你之前确定的 C# + ASP.NET Core + PostgreSQL + EF Core + Redis + Next.js + TypeScript + Bun + Docker 技术栈,给出可以逐文件创建、编译、运行和部署的实际工程代码。
帮我下个简单的项目部署文档,方便我直播部署
可以。下面给你一份适合直播演示/录屏部署的简化版文档。为了直播稳定,我建议不要一开始就部署完整的支付、监控、CI/CD,而是先把 ASP.NET Core + PostgreSQL + Redis + Next.js 跑起来,确认 License API 能正常工作,再逐步扩展。
License System 简易部署文档
1. 部署架构
直播时直接展示这一套:
Internet
│
▼
Nginx / Caddy
│
┌────────────┴────────────┐
│ │
▼ ▼
ASP.NET Core API Next.js Admin
│
┌───┴────┐
▼ ▼
PostgreSQL Redis
服务器建议:
OS: Debian 13 / Ubuntu 24.04 LTS
CPU: 2 Core+
RAM: 4 GB+
Disk: 40 GB+
Docker: 最新稳定版
如果只是直播演示:
2 Core
4 GB RAM
40 GB SSD
基本够用。
2. 项目目录
服务器统一:
/opt/license-platform
结构:
/opt/license-platform/
├── docker-compose.yml
├── .env
├── postgres/
├── redis/
├── backups/
└── nginx/
以后正式生产再增加:
monitoring/
deploy/
scripts/
3. 安装 Docker
Ubuntu / Debian:
sudo apt update
sudo apt install -y ca-certificates curl git
安装 Docker:
curl -fsSL https://get.docker.com | sudo sh
检查:
docker --version
docker compose version
应该看到:
Docker version ...
Docker Compose version ...
4. 创建项目目录
sudo mkdir -p /opt/license-platform
sudo chown -R $USER:$USER /opt/license-platform
进入:
cd /opt/license-platform
5. 创建 .env
nano .env
直播演示可以使用:
POSTGRES_DB=license_db
POSTGRES_USER=license_user
POSTGRES_PASSWORD=CHANGE_THIS_PASSWORD
REDIS_PASSWORD=CHANGE_THIS_REDIS_PASSWORD
ASPNETCORE_ENVIRONMENT=Production
真实生产环境不要直接使用这组示例密码。
建议生成:
openssl rand -base64 32
例如:
POSTGRES_PASSWORD=这里填写生成的随机密码
REDIS_PASSWORD=这里填写生成的随机密码
6. Docker Compose
创建:
nano docker-compose.yml
先使用这个简化版本:
services:
postgres:
image: postgres:17
container_name: license-postgres
restart: unless-stopped
environment:
POSTGRES_DB: ${POSTGRES_DB}
POSTGRES_USER: ${POSTGRES_USER}
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
volumes:
- postgres_data:/var/lib/postgresql/data
networks:
- license_network
healthcheck:
test:
[
"CMD-SHELL",
"pg_isready -U ${POSTGRES_USER} -d ${POSTGRES_DB}"
]
interval: 10s
timeout: 5s
retries: 5
redis:
image: redis:7-alpine
container_name: license-redis
restart: unless-stopped
command:
- redis-server
- --requirepass
- ${REDIS_PASSWORD}
volumes:
- redis_data:/data
networks:
- license_network
healthcheck:
test:
[
"CMD",
"redis-cli",
"-a",
"${REDIS_PASSWORD}",
"ping"
]
interval: 10s
timeout: 5s
retries: 5
license-server:
image: your-registry/license-server:latest
container_name: license-server
restart: unless-stopped
environment:
ASPNETCORE_ENVIRONMENT: ${ASPNETCORE_ENVIRONMENT}
ConnectionStrings__DefaultConnection: >-
Host=postgres;
Port=5432;
Database=${POSTGRES_DB};
Username=${POSTGRES_USER};
Password=${POSTGRES_PASSWORD}
ConnectionStrings__Redis: >-
redis:6379,password=${REDIS_PASSWORD}
depends_on:
postgres:
condition: service_healthy
redis:
condition: service_healthy
ports:
- "8080:8080"
networks:
- license_network
volumes:
postgres_data:
redis_data:
networks:
license_network:
driver: bridge
7. 启动数据库
第一次直播建议先:
docker compose pull
然后:
docker compose up -d postgres redis
检查:
docker compose ps
应该看到:
license-postgres
license-redis
状态:
healthy
8. 测试 PostgreSQL
进入数据库:
docker exec -it license-postgres \
psql -U license_user -d license_db
执行:
SELECT version();
退出:
\q
9. 测试 Redis
docker exec -it license-redis \
redis-cli -a "你的Redis密码"
执行:
PING
返回:
PONG
退出:
exit
10. 部署 ASP.NET Core
如果你的镜像已经上传到 Registry:
docker compose pull license-server
然后:
docker compose up -d license-server
检查:
docker compose ps
查看日志:
docker compose logs -f license-server
看到类似:
Now listening on: http://[::]:8080
Application started
说明API已经启动。
11. 测试 API
服务器执行:
curl http://127.0.0.1:8080/health
如果你的健康检查接口是:
/health
应该返回:
{
"status": "Healthy"
}
12. 如果还没有 Docker 镜像
开发机直接构建:
docker build \
-t license-server:local \
./apps/license-server
查看:
docker images
然后运行:
docker run --rm \
license-server:local
不过直播时我更推荐:
GitHub
↓
GitHub Actions
↓
Docker Image
↓
GHCR
↓
服务器 docker pull
这样演示会更加接近真正的商业部署流程。
13. Nginx反向代理
安装:
sudo apt install -y nginx
创建:
sudo nano /etc/nginx/sites-available/license
简单配置:
server {
listen 80;
server_name api.example.com;
location / {
proxy_pass http://127.0.0.1:8080;
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;
}
}
启用:
sudo ln -s \
/etc/nginx/sites-available/license \
/etc/nginx/sites-enabled/license
检查:
sudo nginx -t
然后:
sudo systemctl reload nginx
14. HTTPS
直播环境最简单:
Cloudflare
↓
Nginx
↓
ASP.NET Core
如果使用Let’s Encrypt:
sudo apt install -y certbot python3-certbot-nginx
然后:
sudo certbot --nginx \
-d api.example.com
测试:
curl https://api.example.com/health
15. PostgreSQL绝对不要开放公网
检查:
sudo ss -lntp
不要出现:
0.0.0.0:5432
也不要:
0.0.0.0:6379
正确应该是:
Internet
│
▼
Nginx :443
│
▼
API :8080
│
├── PostgreSQL
└── Redis
PostgreSQL和Redis只存在于Docker内部网络。
16. EF Core Migration
进入API容器:
docker exec -it license-server bash
如果容器内包含EF工具,可以:
dotnet ef database update
更推荐正式生产:
Build
↓
Migration
↓
Backup
↓
Migration
↓
Deploy
而不是每次API启动自动修改生产数据库。
17. 第一次启动完整流程
直播时直接按照下面执行即可:
cd /opt/license-platform
然后:
docker compose pull
启动:
docker compose up -d postgres redis
检查:
docker compose ps
然后:
docker compose up -d license-server
检查:
docker compose ps
查看:
docker compose logs -f license-server
最后:
curl http://127.0.0.1:8080/health
18. 停止系统
docker compose stop
注意:
stop
不会删除数据库。
19. 重启系统
docker compose restart
或者:
docker compose up -d
20. 查看日志
全部:
docker compose logs
实时:
docker compose logs -f
只看API:
docker compose logs -f license-server
只看数据库:
docker compose logs -f postgres
21. 更新版本
以后发布:
v1.0.1
服务器:
docker compose pull
然后:
docker compose up -d
检查:
docker compose ps
最后:
docker compose logs -f license-server
22. 数据库备份
创建:
mkdir -p /opt/license-platform/backups
执行:
docker exec license-postgres \
pg_dump \
-U license_user \
-d license_db \
> /opt/license-platform/backups/license_$(date +%Y%m%d_%H%M%S).sql
查看:
ls -lh backups/
23. 数据库恢复
例如:
backups/license_20260811_120000.sql
执行:
cat backups/license_20260811_120000.sql | \
docker exec -i license-postgres \
psql -U license_user -d license_db
正式生产恢复前应先停止写入,并优先在恢复环境验证备份。
24. 直播部署检查表
直播前先准备:
☐ 云服务器
☐ 域名
☐ DNS
☐ Docker
☐ Git
☐ Docker Registry
☐ PostgreSQL
☐ Redis
☐ License Server镜像
☐ .env
☐ Nginx
☐ HTTPS
直播过程中:
☐ 创建服务器
☐ 安装Docker
☐ Clone项目
☐ 配置.env
☐ 启动PostgreSQL
☐ 启动Redis
☐ 启动License Server
☐ 执行Migration
☐ 测试/health
☐ 配置Nginx
☐ 配置HTTPS
☐ 测试公网API
☐ 创建License
☐ 客户端激活
25. 最适合直播演示的最终效果
直播最后可以展示:
浏览器
│
▼
https://api.example.com/health
│
▼
┌──────────────────┐
│ ASP.NET Core API │
└────────┬─────────┘
│
┌────┴────┐
▼ ▼
PostgreSQL Redis
然后:
Admin Console
│
▼
创建 License
│
▼
48D0-61CD-35FE-9C9B...
│
▼
Windows / macOS / Linux
│
▼
Activate
│
▼
License Server
│
▼
Activation Successful
这样一场直播就能把**“服务器部署 → 数据库 → API → License生成 → 客户端激活”**完整跑通。
**建议你直播时先不要把支付、自动更新、KMS/HSM、监控集群等复杂部分放进去。**先完成这一条最核心的商业闭环,后面再逐个加入。
把这个也写出来吧,方便我以后补充
可以。建议把这部分单独保存成项目里的 《第十一部分:生产环境扩展部署手册》,以后你开发到支付、Admin、自动更新、监控等模块时,可以直接往里面补充,不需要重新整理整套部署文档。
下面我直接给你一份可作为项目正式文档的版本。
第十一部分:生产环境扩展部署手册
文档版本:v1.0 适用项目:License Management Platform 技术栈:ASP.NET Core + PostgreSQL + Redis + Next.js + TypeScript + Bun + Docker 目标环境:Debian / Ubuntu Linux 开发环境:Windows / macOS / Linux
1. 文档目的
本章节用于记录 License 商业授权系统从基础部署逐步扩展到完整生产环境所需要的部署配置。
基础部署完成后,可以按照以下顺序逐步增加:
基础 API
↓
Admin Console
↓
Customer Portal
↓
Payment
↓
自动发货
↓
Email
↓
HTTPS
↓
Monitoring
↓
Backup
↓
CI/CD
↓
Auto Update
↓
Security Hardening
↓
Production HA
不要求第一次部署全部完成。
2. 推荐生产架构
最终生产环境:
Internet
│
▼
Cloudflare
│
WAF / DDoS
│
▼
Nginx
│
┌───────────────┼────────────────┐
│ │ │
▼ ▼ ▼
License API Admin Web Customer Web
│
┌────┴─────────────┐
│ │
▼ ▼
PostgreSQL Redis
│
▼
Backup / Object Storage
后台任务:
Payment Worker
Email Worker
License Worker
Update Worker
Cleanup Worker
监控:
ASP.NET Core
│
▼
OpenTelemetry
│
┌────┼─────┐
▼ ▼ ▼
Logs Metrics Traces
│ │ │
▼ ▼ ▼
Loki Prometheus Tempo
│
▼
Grafana
3. 服务器规划
3.1 开发环境
开发机:
Windows
macOS
Linux
本地服务:
PostgreSQL
Redis
License API
Admin Console
推荐全部使用 Docker。
3.2 Staging
建议单独建立:
staging.example.com
环境:
API
PostgreSQL
Redis
Admin
Payment Sandbox
用于:
功能测试
升级测试
Migration测试
支付Webhook测试
License并发测试
3.3 Production
生产环境:
api.example.com
admin.example.com
app.example.com
download.example.com
status.example.com
生产数据库:
PostgreSQL
生产缓存:
Redis
4. 环境变量管理
禁止把生产密钥提交到 Git。
错误:
.env
appsettings.Production.json
直接提交:
git add .
git commit
正确:
Git
│
├── Source Code
└── Configuration Template
Production
│
└── Secret Manager
5. 环境变量分类
建议:
Database
Redis
JWT
License Signing
Payment
Email
Cloud Storage
Monitoring
例如:
POSTGRES_HOST=postgres
POSTGRES_DATABASE=license
POSTGRES_USERNAME=license
POSTGRES_PASSWORD=
REDIS_HOST=redis
REDIS_PASSWORD=
PAYMENT_SECRET=
PAYMENT_WEBHOOK_SECRET=
SMTP_HOST=
SMTP_USERNAME=
SMTP_PASSWORD=
LICENSE_SIGNING_KEY=
6. Secret Management
开发环境:
.env
Staging:
GitHub Actions Secrets
Production:
优先:
Cloud Secret Manager
或者:
HashiCorp Vault
大型商业部署:
KMS / HSM
7. License私钥管理
这是整个系统最重要的Secret之一。
例如:
License Signing Private Key
禁止:
❌ Git
❌ Docker Image
❌ Source Code
❌ Client
❌ Public Download
正确:
License Server
│
▼
Secret Manager / KMS
│
▼
Private Key
客户端只保存:
Public Key
8. PostgreSQL生产配置
生产数据库:
不开放公网
Docker网络:
license_network
只有:
License API
Worker
Admin Backend
可以连接数据库。
禁止:
0.0.0.0:5432
9. PostgreSQL连接池
ASP.NET Core配置:
Maximum Pool Size
Minimum Pool Size
Connection Timeout
Command Timeout
不要无限增加连接数。
服务器:
2 CPU / 4GB RAM
可以从较小连接池开始,根据压力测试调整。
10. Redis生产配置
Redis用于:
Rate Limiting
Distributed Cache
Session
Temporary Data
Idempotency
Distributed Lock
Redis同样:
❌ 不开放公网
11. Redis数据原则
不要把唯一重要数据只放Redis。
错误:
License
↓
Redis
正确:
License
↓
PostgreSQL
Cache
↓
Redis
Redis丢失后:
系统可以重新从PostgreSQL恢复
12. Admin Console部署
Next.js项目:
apps/admin-console
构建:
bun install
bun run build
Docker:
docker build -t license-admin .
运行:
Browser
↓
Nginx
↓
Next.js
↓
License API
Admin不要直接访问PostgreSQL。
13. Customer Portal部署
Customer Portal:
app.example.com
用户可以:
Login
View License
View Orders
View Devices
Deactivate Device
Download Software
View Subscription
同样:
Customer Portal
↓
License API
↓
PostgreSQL
14. Admin权限
至少划分:
SuperAdmin
Admin
Support
Finance
Developer
ReadOnly
例如:
SuperAdmin
│
├── License
├── Users
├── Orders
├── Payments
├── System
└── Security
Support:
License查询
Customer查询
Device管理
但不能:
查看Signing Private Key
修改系统Security配置
15. Admin MFA
生产环境:
Admin Login
↓
Password
↓
MFA
↓
Session
推荐:
TOTP
Passkey
Security Key
禁止Admin仅依赖:
Username + Password
16. Payment部署
支付流程:
Customer
↓
Checkout
↓
Payment Provider
↓
Payment Success
↓
Webhook
↓
License Server
↓
Create License
↓
Email
重点:
不要根据浏览器跳转页面判断支付成功。
必须以:
Verified Webhook
作为最终依据。
17. Webhook安全
Webhook必须验证:
Signature
Timestamp
Event ID
同时保存:
WebhookEvent
建立:
Unique(EventId)
防止:
同一个Webhook
重复处理
18. 支付幂等性
例如:
Payment Success
Webhook发送三次:
Webhook #1
Webhook #2
Webhook #3
系统最终只能:
创建一个Order
创建一个License
发送一次激活邮件
不能:
创建三个License
19. 自动发货
订单状态:
Pending
↓
Paid
↓
LicenseCreated
↓
EmailQueued
↓
Delivered
邮件:
Your Order
License Key
Download Link
Activation Guide
邮件发送失败:
Retry
而不是重新创建License。
20. Email Worker
推荐后台Worker:
Email Queue
↓
Email Worker
↓
SMTP / Email Provider
API不要:
HTTP Request
↓
等待SMTP
↓
返回用户
应该:
API
↓
Queue
↓
立即返回
Worker
↓
发送Email
21. Background Worker
推荐:
License.Worker
处理:
Email
Payment
Cleanup
Expiration
Device
Update
Notification
这样API和后台任务职责分离。
22. License过期任务
Worker周期执行:
Every 5 minutes
检查:
ExpiresAt
状态:
Active
↓
Expired
但是不要依赖Worker判断授权是否有效。
API本身仍然必须检查:
ExpiresAt
Worker只是:
状态维护
通知
清理
23. 自动更新服务器
建立:
download.example.com
目录:
releases/
├── windows/
├── macos/
└── linux/
每个版本:
v1.0.0
v1.0.1
v1.1.0
同时提供:
manifest.json
包含:
Version
Platform
Download
SHA256
Signature
24. Release安全
发布流程:
Build
↓
Test
↓
Generate SHA256
↓
Code Sign
↓
Generate Release Signature
↓
Upload
↓
Publish
客户端:
Download
↓
Hash Verify
↓
Signature Verify
↓
Install
25. Monitoring
生产部署:
Prometheus
Grafana
Loki
OpenTelemetry
最少监控:
API Availability
API Latency
HTTP 5xx
Database
Redis
CPU
Memory
Disk
26. License业务监控
额外监控:
Activation Success
Activation Failure
Device Limit
License Expired
License Revoked
Payment Failure
Webhook Failure
例如:
Activation Failure Rate
突然从:
1%
变成:
20%
应该立即报警。
27. 日志
日志必须包含:
Timestamp
RequestId
UserId
LicenseId
DeviceId
Action
Result
但是:
❌ License完整密钥
❌ Password
❌ API Secret
❌ Payment Secret
❌ Private Key
绝对不能写入日志。
28. Audit Log
重要Admin操作:
Create License
Revoke License
Reset Device
Refund Order
Change Plan
Change User Role
Change System Config
都记录:
Who
What
When
IP
Result
例如:
Admin:
admin@example.com
Action:
Revoke License
License:
LIC-xxxx
Time:
2026-08-11 12:30:00
Result:
Success
29. Backup
生产数据库:
Daily Full Backup
+
Continuous WAL / PITR
备份:
Local
+
Remote Object Storage
例如:
Server
↓
Encrypted Backup
↓
Object Storage
30. Backup测试
每月至少一次:
Backup
↓
New PostgreSQL
↓
Restore
↓
Run API
↓
Test License
必须验证:
License
Order
Customer
Device
Payment
都能恢复。
31. CI/CD
Git Push:
GitHub
↓
GitHub Actions
↓
Build
↓
Test
↓
Security Scan
↓
Docker Build
↓
Push Registry
生产:
Approval
↓
Deploy
32. Docker Registry
推荐:
GHCR
镜像:
ghcr.io/company/license-server
ghcr.io/company/admin-console
版本:
v1.0.0
v1.0.1
同时可以:
latest
但生产部署优先使用:
固定版本
而不是:
latest
33. Production Deployment
标准流程:
git tag v1.0.1
↓
CI Build
↓
Tests
↓
Docker Image
↓
Registry
↓
Backup
↓
Database Migration
↓
Deploy
↓
Health Check
↓
Smoke Test
34. 回滚
部署失败:
v1.0.1
回滚:
v1.0.0
Docker:
docker compose pull
docker compose up -d
使用固定版本:
license-server:v1.0.0
不要依赖:
latest
35. 数据库Migration回滚
数据库Migration必须考虑:
Backward Compatibility
推荐:
Expand
↓
Deploy
↓
Migrate Data
↓
Switch
↓
Contract
不要一次性删除旧字段。
36. Firewall
公网只允许:
80
443
22
如果SSH允许:
22
建议进一步限制来源IP。
绝对不要开放:
5432
6379
37. Docker安全
容器尽量:
Non-root
Read-only filesystem
No unnecessary capabilities
Resource Limits
例如:
CPU Limit
Memory Limit
防止异常程序耗尽整个服务器。
38. API Rate Limit
重点接口:
/login
/license/activate
/license/validate
/password/reset
必须限速。
例如:
IP
+
Account
+
License
+
Device
多维度限制。
39. 防爆破
激活码不能只依赖:
Rate Limit
还应该:
License Hash
+
Rate Limit
+
IP Reputation
+
Device Fingerprint
+
Audit
同时避免返回过于详细的错误:
错误:
License存在,但Device已经达到上限
在某些接口场景下可能泄露信息。
根据接口用途选择合适的错误粒度。
40. WAF
推荐:
Cloudflare
前面:
Cloudflare
↓
WAF
↓
Rate Limit
↓
Nginx
↓
ASP.NET Core
API服务器不要直接暴露给整个Internet,尽可能让入口集中到WAF/CDN层。
41. DDoS
基础:
Cloudflare
进一步:
Rate Limit
WAF
Caching
Origin Protection
License API一般不缓存敏感响应。
42. Production Security Checklist
☐ HTTPS
☐ HSTS
☐ WAF
☐ Rate Limit
☐ MFA
☐ RBAC
☐ Audit Log
☐ Secret Manager
☐ Database Private
☐ Redis Private
☐ Backup
☐ Restore Test
☐ Dependency Scan
☐ Container Scan
☐ Code Signing
☐ Update Signature
☐ Webhook Verification
☐ Idempotency
☐ Monitoring
☐ Alerting
43. 商业软件最终部署结构
最终:
Internet
│
▼
Cloudflare
│
┌─────────┴─────────┐
│ │
WAF DNS
│
▼
Nginx
│
┌────────────┼─────────────┐
│ │ │
▼ ▼ ▼
API Admin Customer
│
├──────────────┐
│ │
▼ ▼
PostgreSQL Redis
│
▼
Backup
│
▼
Object Storage
后台:
Worker
├── Payment
├── Email
├── License
├── Cleanup
└── Notification
监控:
OpenTelemetry
│
├── Metrics → Prometheus
├── Logs → Loki
└── Traces → Tempo
│
▼
Grafana
44. 最终上线顺序
不要一次性全部上线。
建议:
Phase 1
PostgreSQL
Redis
ASP.NET Core
目标:
API正常
数据库正常
License正常
Phase 2
Admin Console
Customer Portal
Phase 3
Payment
Webhook
Email
Automatic Delivery
Phase 4
Nginx
HTTPS
Cloudflare
WAF
Phase 5
Backup
Monitoring
Alerting
Phase 6
CI/CD
Docker Registry
Automatic Deployment
Phase 7
Auto Update
Code Signing
Windows
macOS
Linux
Phase 8
Security Audit
Load Test
Disaster Recovery
Production Launch
45. 正式商业上线前最终Checklist
Infrastructure
☐ Server
☐ Domain
☐ DNS
☐ Firewall
☐ Docker
Backend
☐ ASP.NET Core
☐ PostgreSQL
☐ Redis
☐ EF Core Migration
☐ Background Worker
Frontend
☐ Admin Console
☐ Customer Portal
☐ Download Center
License
☐ Generate
☐ Activate
☐ Validate
☐ Revoke
☐ Expire
☐ Device Limit
☐ Offline Grace
☐ Signed Token
Payment
☐ Checkout
☐ Webhook
☐ Idempotency
☐ Refund
☐ Automatic License
Email
☐ SMTP/Provider
☐ Activation Email
☐ Password Reset
☐ Order Email
Security
☐ HTTPS
☐ WAF
☐ Rate Limit
☐ MFA
☐ RBAC
☐ Audit Log
☐ Secret Management
☐ Private Database
Client
☐ Windows
☐ macOS Intel
☐ macOS ARM64
☐ Linux
☐ Code Signing
☐ Auto Update
Operations
☐ Logs
☐ Metrics
☐ Traces
☐ Alerts
☐ Backup
☐ Restore
☐ Disaster Recovery
Release
☐ CI
☐ CD
☐ Docker Registry
☐ Version Tag
☐ Rollback
☐ Release Notes
☐ Documentation
Commercial
☐ Pricing
☐ Terms
☐ Privacy Policy
☐ Refund Policy
☐ Support
☐ Status Page
46. 推荐的项目文档目录
以后整个项目可以整理成:
docs/
│
├── 01-project-overview.md
├── 02-architecture.md
├── 03-development-environment.md
├── 04-database.md
├── 05-license-engine.md
├── 06-client-sdk.md
├── 07-admin-console.md
├── 08-payment.md
├── 09-security.md
├── 10-production-deployment.md
├── 11-production-extensions.md
├── 12-backup-recovery.md
├── 13-monitoring.md
├── 14-ci-cd.md
├── 15-client-release.md
├── 16-troubleshooting.md
└── 17-commercial-launch-checklist.md
这样以后开发到哪个阶段,就直接更新对应文档。
47. 直播部署时使用的最简版本
如果只是直播,不需要执行整个第十一部分。
直接:
cd /opt/license-platform
docker compose pull
docker compose up -d postgres redis
docker compose ps
docker compose up -d license-server
docker compose logs -f license-server
curl http://127.0.0.1:8080/health
然后:
Browser
↓
https://api.example.com/health
↓
ASP.NET Core
↓
PostgreSQL + Redis
这一套就是你的“直播演示部署路径”。
后续正式商业上线,再按照本章逐步增加:
HTTPS
→ WAF
→ Admin
→ Payment
→ Email
→ Backup
→ Monitoring
→ CI/CD
→ Auto Update
→ Security Audit
这样既方便直播,又不会因为一开始部署几十个组件导致现场出现不可控的问题。
以后你开发到具体阶段时,可以直接把这一章继续拆成 “11.1 支付部署、11.2 邮件部署、11.3 监控部署、11.4 CI/CD部署……”,逐项补进去。
💬 评论区
💬 评论列表
⏳ 正在加载评论...
✍️ 留下评论
⏳ 正在加载用户状态...
页面活动热力图
(模拟热力图:红点代表活动强度)