Files
AI_OJ/docs/admin-permission-guide.md
meowrain 1945cc2fb1 feat: 添加管理员权限检查功能和Maven打包配置优化
主要更新:

1. 新增管理员权限检查功能
   - 添加 UserRoleEnum 枚举类统一管理用户角色(USER, ADMIN, BAN)
   - 改进 ContextHolderUtils.isAdmin() 方法,支持不区分大小写的角色比较
   - 更新 UserServiceImpl 使用枚举常量代替硬编码字符串
   - 新增管理员权限使用指南文档 (docs/admin-permission-guide.md)

2. 修复Maven打包配置
   - 配置根POM的spring-boot-maven-plugin默认跳过repackage
   - 为所有服务模块启用repackage,确保可以打包为可执行JAR
   - 修复公共库模块打包失败的问题
   - 涉及服务:gateway, auth, user-service, question-service, file-service, blog-service, upms-biz, ai-service

3. 更新项目文档
   - README.md:添加详细的打包说明、首次克隆准备工作、服务启动顺序等
   - CLAUDE.md:更新项目架构说明和开发指南

4. 重构题目服务责任链结构
   - 将责任链类按功能分类到 question/ 和 submit/ 子目录
   - 新增 QuestionSubmitJudgeInfoEnum 和相关查询功能
   - 改进题目提交服务的实现

5. 其他改进
   - 添加 Feign Token 中继拦截器
   - 更新 AsyncConfig 配置
   - 优化 Jackson 和 Security 配置
2026-01-28 23:01:48 +08:00

253 lines
6.8 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.
# 管理员权限检查功能使用指南
## 功能位置
全局管理员权限检查功能已在 `aioj-backend-common-core` 模块中实现,所有服务模块都可以直接使用。
## 核心类
### 1. UserRoleEnum角色枚举
**位置**: `cn.meowrain.aioj.backend.framework.core.enums.UserRoleEnum`
定义了系统中的所有用户角色:
- `USER` - 普通用户code: "user"
- `ADMIN` - 管理员code: "admin"
- `BAN` - 封禁用户code: "ban"
### 2. ContextHolderUtils上下文工具类
**位置**: `cn.meowrain.aioj.backend.framework.core.utils.ContextHolderUtils`
提供了获取当前登录用户信息的工具方法。
## 使用方法
### 1. 检查当前用户是否为管理员
```java
import cn.meowrain.aioj.backend.framework.core.utils.ContextHolderUtils;
// 在任何需要检查管理员权限的地方
if (ContextHolderUtils.isAdmin()) {
// 管理员才能执行的操作
log.info("当前用户是管理员");
} else {
// 非管理员的处理逻辑
throw new ClientException("无权限执行此操作");
}
```
### 2. 获取当前用户ID
```java
// 获取当前登录用户ID未登录会抛出异常
Long userId = ContextHolderUtils.getCurrentUserId();
// 获取当前用户ID未登录返回null
Long userId = ContextHolderUtils.getCurrentUserIdOrNull();
```
### 3. 获取当前用户角色
```java
// 获取当前用户角色字符串
String role = ContextHolderUtils.getCurrentUserRole();
// 使用枚举进行角色判断
if (UserRoleEnum.isAdmin(role)) {
// 管理员逻辑
}
```
### 4. 检查用户是否已登录
```java
if (ContextHolderUtils.isAuthenticated()) {
// 用户已登录
}
```
## 在Controller中使用示例
### 示例1限制删除操作仅管理员可用
```java
@DeleteMapping("/{id}")
@Operation(summary = "删除题目")
public Result<Void> deleteQuestion(@PathVariable Long id) {
// 检查管理员权限
if (!ContextHolderUtils.isAdmin()) {
throw new ClientException("只有管理员才能删除题目");
}
questionService.deleteById(id);
return Result.success();
}
```
### 示例2根据角色返回不同数据
```java
@GetMapping("/list")
@Operation(summary = "获取题目列表")
public Result<List<QuestionDTO>> listQuestions() {
boolean isAdmin = ContextHolderUtils.isAdmin();
if (isAdmin) {
// 管理员可以看到所有题目(包括未发布的)
return Result.success(questionService.listAll());
} else {
// 普通用户只能看到已发布的题目
return Result.success(questionService.listPublished());
}
}
```
### 示例3限制更新操作仅作者或管理员
```java
@PutMapping("/{id}")
@Operation(summary = "更新题目")
public Result<Void> updateQuestion(
@PathVariable Long id,
@RequestBody QuestionUpdateDTO request) {
Question question = questionService.getById(id);
Long currentUserId = ContextHolderUtils.getCurrentUserId();
boolean isAdmin = ContextHolderUtils.isAdmin();
// 只有题目创建者或管理员可以更新
if (!isAdmin && !question.getCreatorId().equals(currentUserId)) {
throw new ClientException("无权限修改此题目");
}
questionService.updateQuestion(id, request);
return Result.success();
}
```
## 在Service层使用示例
```java
@Service
public class QuestionServiceImpl implements QuestionService {
@Override
public void deleteQuestion(Long id) {
// 在Service层也可以进行权限检查
if (!ContextHolderUtils.isAdmin()) {
throw new ServiceException("权限不足");
}
// 执行删除逻辑
this.removeById(id);
}
}
```
## 使用Spring Security注解推荐
除了手动检查还可以使用Spring Security的注解
```java
import org.springframework.security.access.prepost.PreAuthorize;
@DeleteMapping("/{id}")
@PreAuthorize("hasRole('admin')") // 注意:角色名会自动转换为小写
@Operation(summary = "删除题目")
public Result<Void> deleteQuestion(@PathVariable Long id) {
questionService.deleteById(id);
return Result.success();
}
```
**注意**:使用 `@PreAuthorize` 需要在配置类上启用方法级安全:
```java
@Configuration
@EnableMethodSecurity // Spring Boot 3.x
public class SecurityConfig {
// ...
}
```
## 角色枚举使用
在需要设置或比较角色时,使用枚举常量:
```java
import cn.meowrain.aioj.backend.framework.core.enums.UserRoleEnum;
// 设置用户角色
user.setUserRole(UserRoleEnum.ADMIN.getCode());
// 判断角色
if (UserRoleEnum.isAdmin(user.getUserRole())) {
// 管理员逻辑
}
// 根据代码获取枚举
UserRoleEnum role = UserRoleEnum.fromCode("admin");
if (role == UserRoleEnum.ADMIN) {
// 管理员逻辑
}
```
## 注意事项
1. **大小写不敏感**:角色比较不区分大小写,"admin"、"Admin"、"ADMIN" 都会被识别为管理员
2. **异常处理**`getCurrentUserId()``getCurrentUserRole()` 在用户未登录时会抛出 `IllegalStateException`
3. **安全性**建议在Controller和Service层都进行权限检查实现多层防护
4. **JWT依赖**此功能依赖JWT认证确保请求头中包含有效的JWT token
## 完整示例题目管理Controller
```java
@RestController
@RequestMapping("/api/question")
@RequiredArgsConstructor
public class QuestionController {
private final QuestionService questionService;
@PostMapping
@Operation(summary = "创建题目(仅管理员)")
public Result<Long> createQuestion(@RequestBody QuestionCreateDTO request) {
if (!ContextHolderUtils.isAdmin()) {
throw new ClientException("只有管理员才能创建题目");
}
Long questionId = questionService.create(request);
return Result.success(questionId);
}
@PutMapping("/{id}")
@Operation(summary = "更新题目(仅管理员)")
public Result<Void> updateQuestion(
@PathVariable Long id,
@RequestBody QuestionUpdateDTO request) {
if (!ContextHolderUtils.isAdmin()) {
throw new ClientException("只有管理员才能更新题目");
}
questionService.update(id, request);
return Result.success();
}
@DeleteMapping("/{id}")
@Operation(summary = "删除题目(仅管理员)")
public Result<Void> deleteQuestion(@PathVariable Long id) {
if (!ContextHolderUtils.isAdmin()) {
throw new ClientException("只有管理员才能删除题目");
}
questionService.deleteById(id);
return Result.success();
}
@GetMapping("/{id}")
@Operation(summary = "获取题目详情")
public Result<QuestionDTO> getQuestion(@PathVariable Long id) {
// 普通用户和管理员都可以查看
QuestionDTO question = questionService.getById(id);
return Result.success(question);
}
}
```