技术架构
Think Site Manager 采用前后端分离架构,后端基于 Spring Boot 提供 REST API,前端基于 UmiJS Max 构建管理界面。
总体架构
┌─────────────────────────────────────────────────────────────┐
│ 用户浏览器 │
│ ┌──────────────────────┐ ┌──────────────────────────┐ │
│ │ 管理平台 (UmiJS Max) │ │ 文档站点 (VitePress) │ │
│ │ React + Ant Design │ │ + 认证插件 │ │
│ └──────────┬───────────┘ └────────────┬─────────────┘ │
└─────────────┼──────────────────────────────┼────────────────┘
│ HTTP/REST │ HTTP/REST
▼ ▼
┌─────────────────────────────────────────────────────────────┐
│ Spring Boot (Port 8300) │
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────────┐ │
│ │ JWT 认证 │ │ License │ │ 业务逻辑 │ │ 插件生成器 │ │
│ │ Filter │ │ Filter │ │ Services │ │ Plugin Gen │ │
│ └──────────┘ └──────────┘ └────┬─────┘ └──────────────┘ │
│ │ │
│ ┌──────▼──────┐ │
│ │ MyBatis-Plus│ │
│ └──────┬──────┘ │
└─────────────────────────────────┼───────────────────────────┘
│
▼
┌─────────────────────┐
│ MySQL 8.x │
│ think_site_management│
└─────────────────────┘技术栈
后端
| 技术 | 版本 | 用途 |
|---|---|---|
| JDK | 17 | 运行环境 |
| Spring Boot | 3.3.2 | 应用框架 |
| Spring Security | 6.3.1 | 安全认证 |
| MyBatis-Plus | 3.5.7 | ORM 框架 |
| Knife4j | 4.4.0 | API 文档 (OpenAPI 3) |
| jjwt / java-jwt | 0.9.1 / 3.11.0 | JWT Token 处理 |
| Hutool | 5.7.0 | 工具库 |
| Apache MINA SSHD | 2.13.2 | SSH 客户端 + 服务端(native NIO,长连接复用) |
| sshd-scp | 2.13.2 | SCP 文件传输协议 |
| I2P eddsa | 0.3.0 | ed25519 算法 provider(JDK 17 没自带) |
| BouncyCastle | 1.78.1 | 通用 JCA 兜底(bcprov-jdk18on + bcpkix-jdk18on) |
| Apache Commons Compress | 1.26.0 | tar.gz 打包(UTF-8 跨平台,中文文件名不乱码) |
| Flyway | 10.x | 数据库迁移(V20260619_xxx__*.sql) |
| Maven | 3.x | 构建工具 |
前端
| 技术 | 版本 | 用途 |
|---|---|---|
| Node.js | 18+ | 运行环境 |
| UmiJS Max | ^4.6.57 | React 应用框架 |
| React | 18.x | UI 框架 |
| Ant Design | ^5.4.0 | UI 组件库 |
| ProComponents | ^2.4.4 | 高级业务组件 |
| @ant-design/charts | ^2.6.7 | 数据可视化 |
| pnpm | latest | 包管理器 |
中间件
| 技术 | 版本 | 用途 |
|---|---|---|
| MySQL | 8.x | 关系数据库 |
后端项目结构
backend/
├── pom.xml # 父 POM,统一依赖管理
├── admin/ # 主应用模块
│ ├── pom.xml
│ └── src/main/java/cool/webstudy/admin/
│ ├── AdminApplication.java # Spring Boot 启动类
│ ├── configuration/ # 配置类 (Knife4j, Security, MyBatis-Plus)
│ ├── controller/ # REST Controller
│ │ ├── auth/ # 认证接口
│ │ ├── dashboard/ # 数据看板接口
│ │ ├── invite/ # 邀请码接口
│ │ ├── site/ # 站点/文章/绑定/插件接口
│ │ ├── system/ # License 接口
│ │ └── user/ # 用户接口
│ ├── filter/security/ # JWT + License + SiteToken 过滤器
│ ├── mapper/ # MyBatis-Plus Mapper 接口
│ ├── model/
│ │ ├── dto/ # 请求 DTO
│ │ ├── po/ # 持久化对象 (PO)
│ │ └── vo/ # 响应 VO
│ ├── service/ # 业务服务接口 + 实现
│ └── utils/ # 工具类 (JWT, AES, License)
├── common/ # 共享模块
│ └── src/main/java/cool/webstudy/common/
│ ├── constant/enums/ # 通用枚举
│ ├── model/ # 共享 PO/DTO
│ └── utils/ # 通用工具 (UUID)
└── license-gen/ # License 生成 CLI 工具Maven 多模块设计
common 模块不依赖 Spring,可独立被其他 Java 项目引用。admin 模块依赖 common,包含全部业务逻辑。license-gen 是独立 JAR,用于离线生成 .lic 授权文件。
前端项目结构
frontend/
├── .umirc.ts # UmiJS 配置 (路由、代理、插件)
├── package.json
├── src/
│ ├── app.tsx # 运行时配置 (初始化、Layout、请求拦截)
│ ├── access.ts # 权限定义
│ ├── pages/ # 页面组件
│ │ ├── Home/ # 首页数据看板
│ │ ├── Site/ # 站点管理 (含 Detail)
│ │ ├── Article/ # 文章管理
│ │ ├── User/ # 人员管理
│ │ ├── InviteCode/ # 邀请码管理
│ │ ├── Profile/ # 个人信息
│ │ ├── Login/ # 登录页
│ │ ├── Register/ # 注册页
│ │ └── Activate/ # License 激活页
│ ├── components/ # 共享组件
│ ├── services/site/ # API 请求服务层
│ ├── models/ # 全局状态 (Umi plugin-model)
│ └── constants/ # 常量定义
└── mock/ # 开发 Mock 数据页面路由
| 路由 | 组件 | 权限 | 说明 |
|---|---|---|---|
/home | Home | 已登录 | 数据看板 |
/site | Site | 站点管理权限 | 站点列表 |
/site/:unCode | Site/Detail | 站点管理权限 | 站点详情 |
/article | Article | 站点管理权限 | 文章管理 |
/user | User | 用户管理权限 | 人员管理 |
/invite-code | InviteCode | 站点管理权限 | 邀请码管理 |
/profile | Profile | 已登录 | 个人信息 |
/login | Login | 公开 | 登录页 |
/register | Register | 公开 | 注册页 |
/activate | Activate | 公开 | License 激活 |
数据库设计
数据库名称:think_site_management,共 5 张业务表。
设计特点
- 🔑 业务主键 + 自增主键:
un_code(UUID) 作为对外暴露的业务标识,id作为内部自增主键 - ♻️ 逻辑删除:所有表通过
del_flag字段标记删除(0=正常,1=已删),数据不可逆 - 🔒 密码加密:用户密码使用 BCrypt 加密存储
- 🔗 软关联:表之间通过
un_code字段关联,而非数据库外键
安全体系
多层过滤链
请求 → LicenseFilter → SiteTokenFilter → JWTAuthenticationFilter → Controller| 过滤器 | 优先级 | 职责 |
|---|---|---|
LicenseFilter | 最高 | 拦截所有请求,验证 License 有效性,无效则返回激活提示 |
SiteTokenAuthenticationFilter | 中 | 通过 X-Site-Token 头校验第三方站点身份 |
JWTAuthenticationFilter | 最低 | 校验 Bearer Token,解析用户身份写入 SecurityContext |
JWT 认证流程
- 用户通过
/api/user/login登录,返回加密 JWT Token - 前端存储 Token 到 localStorage
- 后续请求通过
Authorization: Bearer <token>头携带 JWTAuthenticationFilter解析 Token,获取用户信息注入 Spring Security Context- JWT Payload 中的用户标识通过 AES 加密保护
License 授权系统
- 🔑 RSA 签名:License 文件包含 RSA 数字签名,防止伪造
- 💻 硬件绑定:支持绑定服务器 MAC 地址和 IP 地址
- ⏰ 定时校验:每小时自动重新验证 License 有效期
- ⛔ 过期保护:License 失效后所有接口返回
LICENSE_INVALID
主机凭据 AES-256-GCM 加密
SSH 密码 / 私钥 / passphrase 等敏感凭据,用 AES-256-GCM 加密存 host 表(详见 主机管理):
- 主密钥
THINK_HOST_CRYPTO_KEY(环境变量,32 字节 hex)启动校验 fail-fast - IV 12 字节随机,每条记录独立
- 密文格式
base64(IV || ciphertext || GCM tag) - 解密点 内存中解密,绝不写日志 / 响应
- 算法 AES-256-GCM(GCM 提供认证加密,防篡改)
主机管理与发布
平台通过 Apache MINA SSHD(异步 NIO SSH 客户端)与目标主机通信,围绕 Host / HostDeployHistory 两张表展开。
核心组件
| 组件 | 职责 |
|---|---|
HostCryptoService | AES-256-GCM 加解密,启动校验 THINK_HOST_CRYPTO_KEY |
SecurityProviderInitializer | 注册 I2P EdDSA (pos 1) + BouncyCastle (pos 2) |
SshConnection | MINA SSHD 封装,长连接复用,暴露 exec / scpClient / openShellChannel |
HostService | 主机 CRUD + 密文存取 + 连通性状态回写 |
HostProbeService | SSH 探测 OS / nginx 状态,3 段鲁棒判断 not-installed / active / inactive |
NginxInitService | 9 步流水线(SSH→装包→启服务→校验→平台主动 HTTP 探测→防火墙→放行→重测),SSE 实时推 |
HostDeployService | 7 步发布流水线(build→SCP→解压→reload),ReentrantLock 串行化同 host+site 并发,ReentrantLock 跨子线程复制 SecurityContext |
HostDeployProgressHub | 复用 BuildProgressHub SSE,额外封装 emitProgress / emitDone / emitFailed |
HostShellWebSocketHandler | xterm.js 端 WebSocket 升级处理,PTY 透传,query token 鉴权 |
PlatformHttpProbe | java.net.http.HttpClient 主动 GET,模拟外部用户访问,验证 nginx 是否真的对外暴露 |
9 步初始化流程
CONNECT → PROBE_OS → INSTALL_NGINX → ENABLE_NGINX → VERIFY_NGINX
→ PROBE_HTTP(平台主动) → CHECK_FIREWALL → OPEN_FIREWALL → REPROBE_HTTP
→ SUCCESS / PARTIAL / ERROR关键设计: HTTP 连通性探测走"平台主动 HTTP GET http://<host>:80/",而不是 SSH 内 curl 127.0.0.1:80。这才验证了「外部用户能不能访问」,而非「主机自己能不能访问」。详见 nginx 初始化 9 步详解。
7 步发布流程
启动 → SSH 预检 → 复用/构建 → SCP 上传 → 解压 webRoot → reload nginx → 清理临时
→ SUCCESS简化部署模型: 不维护多版本目录 + current 软链 + think-sites.conf,直接解压到主机 webRoot(/var/www/html)覆盖原内容,nginx 默认 server 的 root 就是这个目录,reload 即可生效。详见 站点发布到主机。
跨平台中文文件名
构建产物(VitePress 输出)可能含中文文件名(快速开始.md、guide/入门/)。平台在 Windows 上跑,默认 GBK 编码,Java ZipOutputStream 写入 zip 时如果 entry name 是 GBK char 序列,会被双重编码,Linux 主机 unzip 拿到错的字节,显示乱码。
修法: 改用 Apache Commons Compress 打 tar.gz:
new TarArchiveOutputStream(gzipOut, "UTF-8") // 构造器第 4 参数显式 UTF-8 编码
.setAddPaxHeadersForNonAsciiNames(true) // 对非 ASCII entry 加 pax extended header任何版本 GNU tar 都能正确解码(旧版读 pax,新版直接读 UTF-8 字节)。旧 .zip 产物走 unzip -O UTF-8 兜底。
SSH 资源约束
Linux server 默认 MaxSessions=10(ubuntu/centos/rocky)。整个 init 流程的 SSH 命令合并为最少 channel(9 个左右):
| 优化 | channel 数 |
|---|---|
prepareNginxDirs(合并 webRoot + conf.d + include 校验) | 1 |
openFirewallPorts(80, 443)(合并放行 + reload) | 1 |
diagnostics()(合并 IP + 监听 + 防火墙端口,用 ---LABEL--- 分段) | 1 |
避免触发 SshChannelOpenException: open failed。调大 MaxSessions: sudo sed -i 's/^#MaxSessions 10/MaxSessions 50/' /etc/ssh/sshd_config && sudo systemctl restart sshd。
API 设计规范
统一响应格式
所有 API 返回统一格式:
{
"success": true,
"code": "00000",
"message": "操作成功",
"data": { }
}分页响应
{
"success": true,
"data": {
"records": [],
"current": 1,
"pages": 10,
"size": 20,
"total": 200
}
}API 文档
开发环境启动后端后,可通过 Knife4j 访问在线 API 文档:
- Swagger UI:
http://localhost:8300/api/doc.html - OpenAPI JSON:
http://localhost:8300/api/v3/api-docs
代码规范体系
项目目录 rules/ 下包含完整的开发规范文档:
| 规范 | 覆盖范围 |
|---|---|
project-structure.md | 总体架构、模块划分、目录结构 |
naming-conventions.md | 类名、方法名、变量名、包名规范 |
database-design.md | 表设计、字段类型、索引、逻辑删除 |
backend-standards.md | Controller/Service/Mapper 分层规范 |
frontend-standards.md | 组件、服务、路由、状态管理规范 |
api-design.md | REST 风格、请求/响应格式、错误码 |
下一步
了解了技术架构后,可以继续阅读 部署说明 开始部署平台。