feat: 多用户管理与公开注册功能

- 后端:User 实体新增 role/enabled 字段、UserController CRUD、UserService
- 安全:SecurityConfig /api/users/** 要求 ROLE_ADMIN、JWT 过滤器检查账号状态
- 注册:POST /api/auth/register 公开注册,固定 ROLE_USER
- 保护:删除/禁用/降级最后 admin 均拒绝,DataInitializer 含 backfill
- 前端:用户管理页面、登录/注册切换、admin 专属导航入口
- 测试:UserServiceTest 19 个 + UserControllerTest 6 个 + AuthControllerTest 适配
This commit is contained in:
liumangmang
2026-05-28 09:13:27 +08:00
parent d038dabc44
commit 1f1d1db65a
25 changed files with 1800 additions and 80 deletions
+138
View File
@@ -0,0 +1,138 @@
# 多用户管理功能 — 实现计划
## 目标
当前项目只支持一个硬编码的 `admin` 用户。本计划将其升级为完整的**多用户管理体系**,支持管理员创建/编辑/删除普通用户、密码重置、用户列表展示、用户启用/禁用,以及权限分离(管理员 vs 普通用户)。
---
## 实施步骤
### 步骤 1:后端 — User 实体增加字段
**文件**: `backend/src/main/java/com/sshmanager/entity/User.java`
| 新增字段 | 类型 | 说明 |
|---------|------|------|
| `role` | `String` | `"ROLE_ADMIN"``"ROLE_USER"`,默认 `"ROLE_USER"` |
| `enabled` | `boolean` | 是否启用,默认 `true` |
同时为 `role``enabled` 添加数据库列映射。
---
### 步骤 2:后端 — 创建 User 相关 DTO
**新建文件**:
| 文件 | 说明 |
|------|------|
| `backend/src/main/java/com/sshmanager/dto/UserDto.java` | 返回给前端的用户信息(不含密码) |
| `backend/src/main/java/com/sshmanager/dto/CreateUserRequest.java` | 创建用户请求体 |
| `backend/src/main/java/com/sshmanager/dto/UpdateUserRequest.java` | 更新用户请求体 |
---
### 步骤 3:后端 — 创建 UserService
**新建文件**: `backend/src/main/java/com/sshmanager/service/UserService.java`
方法:
- `listUsers()` — 列出所有用户(仅 admin
- `getUser(id)` — 获取单个用户
- `createUser(request)` — 创建用户,校验 username 唯一性
- `updateUser(id, request)` — 更新用户 displayName / role / enabled
- `deleteUser(id)` — 删除用户(不能删除自己,不能删除最后一个 admin)
- `resetPassword(id, newPassword)` — 管理员重置指定用户密码
---
### 步骤 4:后端 — 创建 UserController
**新建文件**: `backend/src/main/java/com/sshmanager/controller/UserController.java`
REST API (`/api/users`)
| 方法 | 路径 | 权限 | 说明 |
|------|------|------|------|
| GET | `/api/users` | ADMIN | 用户列表 |
| GET | `/api/users/{id}` | ADMIN | 用户详情 |
| POST | `/api/users` | ADMIN | 创建用户 |
| PUT | `/api/users/{id}` | ADMIN | 更新用户 |
| DELETE | `/api/users/{id}` | ADMIN | 删除用户 |
| POST | `/api/users/{id}/reset-password` | ADMIN | 重置密码 |
---
### 步骤 5:后端 — Security / 权限改造
**文件**:
- `CustomUserDetailsService.java` — 在 `UserDetails` 中赋予 `role` 对应的 `SimpleGrantedAuthority`
- `SecurityConfig.java` — 添加 `antMatchers("/api/users/**").hasRole("ADMIN")`
- `AuthController.java` — 修改 `/auth/me` 返回 `role` 字段
- `LoginResponse.java` — 添加 `role` 字段
- `DataInitializer.java` — admin 初始角色设为 `ROLE_ADMIN`
**新建文件**:
- `backend/src/main/java/com/sshmanager/security/AdminOnlyAspect.java` (可选,或直接在 Controller 里通过 Authentication 检查)
---
### 步骤 6:前端 — 类型和服务
**文件**:
- `frontend/src/types.ts``LoginResponse``CurrentUser` 增加 `role: string`
- `frontend/src/services/users.ts` — 用户 CRUD HTTP 服务
---
### 步骤 7:前端 — 用户管理页面
**新建文件**: `frontend/src/pages/UserManagementPage.tsx`
功能:
- 用户列表表格(用户名、显示名、角色、状态、创建时间)
- 创建用户弹窗(用户名、密码、显示名、角色选择)
- 编辑用户弹窗(修改显示名、角色、启用/禁用)
- 删除用户(确认 + 不能删除自己)
- 重置密码弹窗
---
### 步骤 8:前端 — 路由 & 导航接入
**文件**:
- `App.tsx` — 添加 `/users` 路由指向 `UserManagementPage`
- `WorkspacePage.tsx` — 顶部导航栏增加"用户管理"按钮,仅当当前用户 `role === "ROLE_ADMIN"` 时显示
---
## 数据流
```
[UserManagementPage] --GET /api/users--> [UserController] --UserService--> [UserRepository] --> [DB]
--POST /api/users-->
--PUT /api/users/{id}-->
--DELETE /api/users/{id}-->
--POST /api/users/{id}/reset-password-->
```
每个连接(Connection)已经通过 `userId` 与用户关联,同一用户仅能看到自己的连接。多用户后此逻辑保持不变。
---
## 风险 & 注意事项
1. **不能删除最后一个 admin** — UserService 中 deleteUser 需检查
2. **不能禁用自己** — updateUser 如果自己禁用自己的 enabled=false 需拒绝
3. **密码重置后应强制改密** — 重置时设 `passwordChangedAt = Instant.EPOCH`,使 `isPasswordChangeRequired` 返回 true
4. **H2 数据库** — 重启后数据消失但 DataInitializer 会重建 admin;正式部署应考虑迁移到持久化数据库
---
## 验证方式
1. 启动后端,`curl /api/users` 应返回 admin 用户
2. 用新用户登录验证数据隔离(自己只能看到自己的连接)
3. admin 删除最后一个 admin 用户应拒绝