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 配置
This commit is contained in:
252
docs/admin-permission-guide.md
Normal file
252
docs/admin-permission-guide.md
Normal file
@@ -0,0 +1,252 @@
|
||||
# 管理员权限检查功能使用指南
|
||||
|
||||
## 功能位置
|
||||
|
||||
全局管理员权限检查功能已在 `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);
|
||||
}
|
||||
}
|
||||
```
|
||||
Reference in New Issue
Block a user