Files
gamegroup/权限管理文档.md
UGREEN USER b25aa5b143 初始化游戏小组管理系统后端项目
- 基于 NestJS + TypeScript + MySQL + Redis 架构
- 完整的模块化设计(认证、用户、小组、游戏、预约等)
- JWT 认证和 RBAC 权限控制系统
- Docker 容器化部署支持
- 添加 CLAUDE.md 项目开发指南
- 配置 .gitignore 忽略文件

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-01-28 10:42:06 +08:00

686 lines
19 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 权限管理文档
## 一、权限架构概述
本系统采用 NestJS 框架,基于 **JWT 认证 + 角色权限** 的多层权限控制体系。
### 权限层级
```
全局守卫APP_GUARD
├─ JwtAuthGuardJWT 认证守卫)
│ └─ 验证用户身份,检查 Token 有效性
└─ RolesGuard角色守卫
└─ 验证用户角色权限
```
---
## 二、权限角色定义
### 2.1 系统级角色UserRole
定义位置:[src/common/enums/index.ts](src/common/enums/index.ts)
| 角色 | 枚举值 | 描述 | 权限范围 |
|-----|-------|-----|---------|
| **管理员** | `admin` | 系统管理员 | 全部系统功能 |
| **普通用户** | `user` | 普通用户 | 基础功能 |
用户角色存储在 `users` 表的 `role` 字段类型enum
### 2.2 小组级角色GroupMemberRole
定义位置:[src/common/enums/index.ts](src/common/enums/index.ts)
| 角色 | 枚举值 | 描述 | 权限范围 |
|-----|-------|-----|---------|
| **组长** | `owner` | 小组创建者 | 小组所有管理权限 |
| **管理员** | `admin` | 小组管理员 | 大部分管理权限 |
| **普通成员** | `member` | 普通小组成员 | 基础功能 |
小组角色存储在 `group_members` 表的 `role` 字段类型enum
### 2.3 会员权限isMember
- **字段**`users.isMember`(布尔值)
- **到期时间**`users.memberExpireAt`(日期时间)
- **特权**
- 创建更多小组非会员1个会员10个
- 创建子组
- 审核黑名单
- 删除任意黑名单记录
---
## 三、权限控制组件
### 3.1 守卫Guards
#### JwtAuthGuard - JWT 认证守卫
**文件位置**[src/common/guards/jwt-auth.guard.ts](src/common/guards/jwt-auth.guard.ts)
**功能**
- 默认所有接口都需要认证
- 验证 JWT Token 有效性
- 支持通过 `@Public()` 装饰器标记公开接口
**执行逻辑**
```typescript
1. @Public()
2.
3. passport JWT Token
4. UNAUTHORIZED 10006
5. request.user
```
**错误响应**
```json
{
"code": 10006,
"message": "未授权"
}
```
#### RolesGuard - 角色守卫
**文件位置**[src/common/guards/roles.guard.ts](src/common/guards/roles.guard.ts)
**功能**
- 检查用户是否拥有所需的系统角色
- 配合 `@Roles()` 装饰器使用
**执行逻辑**
```typescript
1. @Roles()
2.
3. request.user
4.
5. NO_PERMISSION 20003
```
**错误响应**
```json
{
"code": 20003,
"message": "无权限操作"
}
```
### 3.2 装饰器Decorators
#### @Public() - 公开接口装饰器
**文件位置**[src/common/decorators/public.decorator.ts](src/common/decorators/public.decorator.ts)
**用途**:标记不需要认证的公开接口
**使用示例**
```typescript
@Public()
@Post('login')
async login(@Body() loginDto: LoginDto) {
return this.authService.login(loginDto);
}
```
#### @Roles() - 角色装饰器
**文件位置**[src/common/decorators/roles.decorator.ts](src/common/decorators/roles.decorator.ts)
**用途**:限制接口只允许特定角色访问
**使用示例**
```typescript
@Roles(UserRole.ADMIN)
@Delete(':id')
async deleteUser(@Param('id') id: string) {
return this.usersService.remove(id);
}
```
#### @CurrentUser() - 当前用户装饰器
**文件位置**[src/common/decorators/current-user.decorator.ts](src/common/decorators/current-user.decorator.ts)
**用途**:获取当前登录用户信息
**使用示例**
```typescript
@Get('me')
async getProfile(@CurrentUser() user: User) {
return this.usersService.findOne(user.id);
}
```
---
## 四、权限检查接口分类
### 4.1 公开接口(无需认证)
#### 认证模块Auth
| 方法 | 路径 | 功能 | 权限 |
|------|------|------|------|
| POST | `/auth/register` | 用户注册 | 公开 |
| POST | `/auth/login` | 用户登录 | 公开 |
| POST | `/auth/refresh` | 刷新令牌 | 公开 |
#### 游戏模块Games
| 方法 | 路径 | 功能 | 权限 |
|------|------|------|------|
| GET | `/games` | 获取游戏列表 | 公开 |
| GET | `/games/popular` | 获取热门游戏 | 公开 |
| GET | `/games/tags` | 获取游戏标签 | 公开 |
| GET | `/games/platforms` | 获取游戏平台 | 公开 |
| GET | `/games/:id` | 获取游戏详情 | 公开 |
### 4.2 需要认证的接口JWT
#### 用户模块Users
| 方法 | 路径 | 功能 | 权限要求 |
|------|------|------|---------|
| GET | `/users/me` | 获取当前用户信息 | 登录用户 |
| GET | `/users/:id` | 获取用户信息 | 登录用户 |
| PUT | `/users/me` | 更新用户信息 | 本人 |
| PUT | `/users/me/password` | 修改密码 | 本人 |
#### 小组模块Groups
| 方法 | 路径 | 功能 | 权限要求 |
|------|------|------|---------|
| POST | `/groups` | 创建小组 | 登录用户(有创建限制) |
| POST | `/groups/join` | 加入小组 | 登录用户 |
| GET | `/groups/my` | 获取我的小组 | 登录用户 |
| GET | `/groups/:id` | 获取小组详情 | 登录用户 |
| PUT | `/groups/:id` | 更新小组信息 | Owner/Admin |
| PUT | `/groups/:id/members/role` | 设置成员角色 | Owner/Admin |
| DELETE | `/groups/:id/members` | 踢出成员 | Owner/Admin |
| DELETE | `/groups/:id/leave` | 退出小组 | 本人 |
| DELETE | `/groups/:id` | 解散小组 | Owner |
#### 预约模块Appointments
| 方法 | 路径 | 功能 | 权限要求 |
|------|------|------|---------|
| POST | `/appointments` | 创建预约 | 登录用户 |
| GET | `/appointments` | 获取预约列表 | 登录用户 |
| GET | `/appointments/my` | 获取我的预约 | 登录用户 |
| GET | `/appointments/:id` | 获取预约详情 | 登录用户 |
| POST | `/appointments/join` | 加入预约 | 登录用户 |
| PUT | `/appointments/:id` | 更新预约 | 发起人 |
| PUT | `/appointments/:id/confirm` | 确认预约 | 发起人 |
| PUT | `/appointments/:id/complete` | 完成预约 | 发起人 |
| DELETE | `/appointments/:id/leave` | 退出预约 | 参与者 |
| DELETE | `/appointments/:id` | 取消预约 | 发起人 |
#### 黑名单模块Blacklist
| 方法 | 路径 | 功能 | 权限要求 |
|------|------|------|---------|
| POST | `/blacklist` | 提交举报 | 登录用户 |
| GET | `/blacklist` | 查询黑名单列表 | 登录用户 |
| GET | `/blacklist/check/:targetGameId` | 检查游戏ID | 登录用户 |
| GET | `/blacklist/:id` | 查询记录详情 | 登录用户 |
| PATCH | `/blacklist/:id/review` | 审核黑名单 | 会员 |
| DELETE | `/blacklist/:id` | 删除记录 | 举报人或会员 |
#### 资产模块Assets
| 方法 | 路径 | 功能 | 权限要求 |
|------|------|------|---------|
| POST | `/assets` | 创建资产 | 小组Owner/Admin |
| GET | `/assets/group/:groupId` | 查询小组资产 | 登录用户 |
| GET | `/assets/:id` | 查询资产详情 | 登录用户 |
| PATCH | `/assets/:id` | 更新资产 | 小组Owner/Admin |
| POST | `/assets/:id/borrow` | 借用资产 | 小组成员 |
| POST | `/assets/:id/return` | 归还资产 | 借用人 |
| GET | `/assets/:id/logs` | 查询借还记录 | 登录用户 |
| DELETE | `/assets/:id` | 删除资产 | 小组Owner/Admin |
#### 游戏模块Games - 管理功能)
| 方法 | 路径 | 功能 | 权限要求 |
|------|------|------|---------|
| POST | `/games` | 创建游戏 | 登录用户 |
| PUT | `/games/:id` | 更新游戏 | 登录用户 |
| DELETE | `/games/:id` | 删除游戏 | 登录用户 |
---
## 五、业务层权限检查
### 5.1 小组权限检查
**实现位置**[src/modules/groups/groups.service.ts](src/modules/groups/groups.service.ts)
#### 创建小组限制
```typescript
// 非会员最多1个小组
// 会员最多10个小组
if (!user.isMember && ownedGroupsCount >= 1) {
throw new BadRequestException('非会员最多只能创建1个小组');
}
if (user.isMember && ownedGroupsCount >= 10) {
throw new BadRequestException('会员最多只能创建10个小组');
}
```
#### 创建子组权限
```typescript
// 只有会员才能创建子组
if (createGroupDto.parentId && !user.isMember) {
throw new ForbiddenException('非会员不能创建子组');
}
```
#### 小组管理权限
```typescript
// 检查用户是否为组长或管理员
const member = await this.groupMemberRepository.findOne({
where: { groupId, userId },
});
if (!member || (member.role !== GroupMemberRole.OWNER &&
member.role !== GroupMemberRole.ADMIN)) {
throw new ForbiddenException('需要管理员权限');
}
```
#### 解散小组权限
```typescript
// 只有组长可以解散小组
if (member.role !== GroupMemberRole.OWNER) {
throw new ForbiddenException('只有组长可以解散小组');
}
```
### 5.2 黑名单权限检查
**实现位置**[src/modules/blacklist/blacklist.service.ts](src/modules/blacklist/blacklist.service.ts)
#### 审核权限
```typescript
// 只有会员才能审核黑名单
if (!user || !user.isMember) {
throw new ForbiddenException('需要会员权限');
}
```
#### 删除权限
```typescript
// 举报人或会员可以删除记录
if (blacklist.reporterId !== userId && !user.isMember) {
throw new ForbiddenException('无权限操作');
}
```
### 5.3 资产管理权限检查
**实现位置**[src/modules/assets/assets.service.ts](src/modules/assets/assets.service.ts)
#### 创建/更新/删除资产
```typescript
// 需要是小组的 Owner 或 Admin
const membership = await this.groupMemberRepository.findOne({
where: { groupId, userId },
});
if (!membership ||
(membership.role !== GroupMemberRole.ADMIN &&
membership.role !== GroupMemberRole.OWNER)) {
throw new ForbiddenException('需要管理员权限');
}
```
#### 借用资产
```typescript
// 必须是小组成员才能借用
const membership = await this.groupMemberRepository.findOne({
where: { groupId: asset.groupId, userId },
});
if (!membership) {
throw new ForbiddenException('您不是该小组成员');
}
```
---
## 六、权限相关错误码
定义位置:[src/common/interfaces/response.interface.ts](src/common/interfaces/response.interface.ts)
| 错误码 | 枚举值 | 消息 | 说明 |
|--------|--------|------|------|
| 10004 | `TOKEN_INVALID` | Token无效 | JWT验证失败 |
| 10005 | `TOKEN_EXPIRED` | Token已过期 | JWT已过期 |
| 10006 | `UNAUTHORIZED` | 未授权 | 未登录或认证失败 |
| 20003 | `NO_PERMISSION` | 无权限操作 | 角色权限不足 |
| 60002 | `INVALID_OPERATION` | 无效操作 | 业务逻辑不允许 |
---
## 七、权限配置
### 7.1 全局守卫注册
**文件位置**[src/app.module.ts](src/app.module.ts)
```typescript
providers: [
// 全局 JWT 认证守卫
{
provide: APP_GUARD,
useClass: JwtAuthGuard,
},
// 全局角色守卫
{
provide: APP_GUARD,
useClass: RolesGuard,
},
]
```
**执行顺序**
1. JwtAuthGuard先执行- 验证身份
2. RolesGuard后执行- 验证角色
### 7.2 JWT 策略配置
**文件位置**[src/modules/auth/jwt.strategy.ts](src/modules/auth/jwt.strategy.ts)
从 JWT Payload 中提取用户信息并注入到 `request.user`
---
## 八、权限使用最佳实践
### 8.1 Controller 层
```typescript
// 1. 使用 @Public() 标记公开接口
@Public()
@Post('login')
async login(@Body() loginDto: LoginDto) {
return this.authService.login(loginDto);
}
// 2. 使用 @CurrentUser() 获取当前用户
@Get('me')
async getProfile(@CurrentUser() user: User) {
return this.usersService.findOne(user.id);
}
// 3. 使用 @Roles() 限制角色(目前项目中未使用)
@Roles(UserRole.ADMIN)
@Delete(':id')
async deleteUser(@Param('id') id: string) {
return this.usersService.remove(id);
}
// 4. 使用 @UseGuards() 应用特定守卫
@UseGuards(JwtAuthGuard)
@Controller('users')
export class UsersController {}
```
### 8.2 Service 层
```typescript
// 1. 在业务逻辑中检查权限
async update(userId: string, groupId: string, updateDto: UpdateGroupDto) {
// 检查用户是否有权限
const member = await this.groupMemberRepository.findOne({
where: { groupId, userId },
});
if (!member || member.role === GroupMemberRole.MEMBER) {
throw new ForbiddenException({
code: ErrorCode.NO_PERMISSION,
message: ErrorMessage[ErrorCode.NO_PERMISSION],
});
}
// 执行业务逻辑
// ...
}
// 2. 根据用户角色返回不同数据
async findOne(assetId: string, userId: string) {
const asset = await this.assetRepository.findOne({
where: { id: assetId }
});
// 检查是否为小组管理员,决定是否显示加密信息
const membership = await this.groupMemberRepository.findOne({
where: { groupId: asset.groupId, userId },
});
if (membership?.role === GroupMemberRole.OWNER ||
membership?.role === GroupMemberRole.ADMIN) {
// 解密敏感信息
asset.accountCredentials = this.decrypt(asset.accountCredentials);
} else {
// 隐藏敏感信息
delete asset.accountCredentials;
}
return asset;
}
```
---
## 九、权限扩展建议
### 9.1 当前缺失的权限功能
1. **系统管理员角色未充分利用**
- `UserRole.ADMIN` 已定义但未在Controller层使用 `@Roles()` 装饰器
- 建议:对管理功能使用 `@Roles(UserRole.ADMIN)` 限制
2. **小组级权限检查不够细化**
- 当前只区分 Owner/Admin/Member
- 建议:可以增加更细粒度的权限点(如:财务管理、成员管理等)
3. **缺少权限审计日志**
- 建议:记录敏感操作的权限检查日志
### 9.2 推荐改进
#### 1. 使用 @Roles() 装饰器
```typescript
// 在控制器中明确标注需要管理员权限的接口
@Roles(UserRole.ADMIN)
@Patch(':id/review')
async review(@CurrentUser() user, @Param('id') id: string) {
return this.blacklistService.review(user.id, id, reviewDto);
}
```
#### 2. 创建自定义权限守卫
```typescript
// group-permission.guard.ts
@Injectable()
export class GroupPermissionGuard implements CanActivate {
constructor(private reflector: Reflector) {}
async canActivate(context: ExecutionContext): Promise<boolean> {
const requiredPermissions = this.reflector.get<string[]>(
'permissions',
context.getHandler(),
);
if (!requiredPermissions) {
return true;
}
const request = context.switchToHttp().getRequest();
const user = request.user;
const groupId = request.params.groupId;
// 检查用户在小组中的权限
// ...
return hasPermission;
}
}
```
#### 3. 添加权限装饰器
```typescript
// group-permissions.decorator.ts
export const GroupPermissions = (...permissions: string[]) =>
SetMetadata('permissions', permissions);
// 使用
@GroupPermissions('manage_members', 'manage_assets')
@Put(':groupId/settings')
async updateSettings() {}
```
---
## 十、权限测试建议
### 10.1 单元测试
- 测试 JwtAuthGuard 的认证逻辑
- 测试 RolesGuard 的角色验证
- 测试各 Service 的权限检查方法
### 10.2 集成测试
- 测试未登录访问受保护接口
- 测试普通用户访问管理员接口
- 测试跨小组权限隔离
- 测试会员与非会员权限差异
### 10.3 E2E 测试
- 完整的用户注册-登录-操作流程
- 权限升级后的功能可用性
- Token 过期后的行为
---
## 十一、安全建议
1. **Token 管理**
- JWT Secret 使用环境变量配置
- 设置合理的 Token 过期时间
- 实现 Refresh Token 机制(已实现)
2. **密码安全**
- 使用强加密算法存储密码
- 实施密码强度策略
- 防止暴力破解
3. **敏感信息保护**
- 资产凭据加密存储(已实现)
- 根据权限动态脱敏
- API 响应中排除敏感字段
4. **防护措施**
- 实施请求限流
- 记录异常访问
- 添加 CORS 配置
- 使用 Helmet 中间件
---
## 十二、权限流程图
### JWT 认证流程
```
用户请求 → JwtAuthGuard
检查 @Public()
是 → 直接放行
否 → 验证 JWT Token
有效 → 注入 user 到 request
无效 → 返回 401 错误
RolesGuard
检查 @Roles()
未设置 → 放行
已设置 → 验证用户角色
匹配 → 放行到 Controller
不匹配 → 返回 403 错误
```
### 小组权限检查流程
```
用户操作小组资源
获取小组成员信息
检查成员角色
Owner → 全部权限
Admin → 管理权限(不含解散)
Member → 基础权限
非成员 → 拒绝访问
```
---
## 附录:相关文件清单
### 核心文件
- [src/common/guards/jwt-auth.guard.ts](src/common/guards/jwt-auth.guard.ts) - JWT认证守卫
- [src/common/guards/roles.guard.ts](src/common/guards/roles.guard.ts) - 角色守卫
- [src/common/decorators/public.decorator.ts](src/common/decorators/public.decorator.ts) - 公开接口装饰器
- [src/common/decorators/roles.decorator.ts](src/common/decorators/roles.decorator.ts) - 角色装饰器
- [src/common/decorators/current-user.decorator.ts](src/common/decorators/current-user.decorator.ts) - 当前用户装饰器
- [src/common/enums/index.ts](src/common/enums/index.ts) - 角色枚举定义
- [src/common/interfaces/response.interface.ts](src/common/interfaces/response.interface.ts) - 错误码定义
### 配置文件
- [src/app.module.ts](src/app.module.ts) - 全局守卫注册
- [src/modules/auth/jwt.strategy.ts](src/modules/auth/jwt.strategy.ts) - JWT策略
### 业务模块
- [src/modules/auth/auth.controller.ts](src/modules/auth/auth.controller.ts) - 认证接口
- [src/modules/users/users.controller.ts](src/modules/users/users.controller.ts) - 用户接口
- [src/modules/groups/groups.controller.ts](src/modules/groups/groups.controller.ts) - 小组接口
- [src/modules/groups/groups.service.ts](src/modules/groups/groups.service.ts) - 小组权限检查
- [src/modules/blacklist/blacklist.controller.ts](src/modules/blacklist/blacklist.controller.ts) - 黑名单接口
- [src/modules/blacklist/blacklist.service.ts](src/modules/blacklist/blacklist.service.ts) - 黑名单权限检查
- [src/modules/assets/assets.controller.ts](src/modules/assets/assets.controller.ts) - 资产接口
- [src/modules/assets/assets.service.ts](src/modules/assets/assets.service.ts) - 资产权限检查
---
**文档版本**v1.0
**更新日期**2025-12-20
**维护者**:开发团队