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

6.8 KiB
Raw Blame History

管理员权限检查功能使用指南

功能位置

全局管理员权限检查功能已在 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. 检查当前用户是否为管理员

import cn.meowrain.aioj.backend.framework.core.utils.ContextHolderUtils;

// 在任何需要检查管理员权限的地方
if (ContextHolderUtils.isAdmin()) {
    // 管理员才能执行的操作
    log.info("当前用户是管理员");
} else {
    // 非管理员的处理逻辑
    throw new ClientException("无权限执行此操作");
}

2. 获取当前用户ID

// 获取当前登录用户ID未登录会抛出异常
Long userId = ContextHolderUtils.getCurrentUserId();

// 获取当前用户ID未登录返回null
Long userId = ContextHolderUtils.getCurrentUserIdOrNull();

3. 获取当前用户角色

// 获取当前用户角色字符串
String role = ContextHolderUtils.getCurrentUserRole();

// 使用枚举进行角色判断
if (UserRoleEnum.isAdmin(role)) {
    // 管理员逻辑
}

4. 检查用户是否已登录

if (ContextHolderUtils.isAuthenticated()) {
    // 用户已登录
}

在Controller中使用示例

示例1限制删除操作仅管理员可用

@DeleteMapping("/{id}")
@Operation(summary = "删除题目")
public Result<Void> deleteQuestion(@PathVariable Long id) {
    // 检查管理员权限
    if (!ContextHolderUtils.isAdmin()) {
        throw new ClientException("只有管理员才能删除题目");
    }

    questionService.deleteById(id);
    return Result.success();
}

示例2根据角色返回不同数据

@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限制更新操作仅作者或管理员

@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层使用示例

@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的注解

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 需要在配置类上启用方法级安全:

@Configuration
@EnableMethodSecurity  // Spring Boot 3.x
public class SecurityConfig {
    // ...
}

角色枚举使用

在需要设置或比较角色时,使用枚举常量:

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

@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);
    }
}