# Spring Security + JWT 权限验证使用示例 ## 1. 权限验证注解使用 在 Controller 方法上使用 `@PreAuthorize` 注解进行权限校验: ```java @RestController @RequestMapping("/api/exam") public class ExamController { // 示例1: 检查是否有特定权限 @PreAuthorize("hasAuthority('exam:list')") @PostMapping("/page") public RestResult getExamsByCondition(@RequestBody ExamQueryVO queryVO) { // 只有拥有 'exam:list' 权限的用户才能访问 // ... } // 示例2: 检查是否有多个权限之一(OR关系) @PreAuthorize("hasAnyAuthority('exam:create', 'exam:add')") @PostMapping("/create") public RestResult createExam(@RequestBody ExamCreateVO createVO) { // 拥有 'exam:create' 或 'exam:add' 权限的用户可以访问 // ... } // 示例3: 检查是否同时拥有多个权限(AND关系) @PreAuthorize("hasAuthority('exam:update') and hasAuthority('exam:verify')") @PostMapping("/update") public RestResult updateExam(@RequestBody ExamUpdateVO updateVO) { // 必须同时拥有 'exam:update' 和 'exam:verify' 权限 // ... } // 示例4: 复杂表达式 @PreAuthorize("hasAuthority('exam:delete') or hasRole('ADMIN')") @PostMapping("/delete/{id}") public RestResult deleteExam(@PathVariable String id) { // 拥有 'exam:delete' 权限或者是 ADMIN 角色的用户可以访问 // ... } } ``` ## 2. 权限配置说明 ### 2.1 数据库配置 **doctor_menu 表结构:** ```sql CREATE TABLE doctor_menu ( id VARCHAR(50) PRIMARY KEY, name VARCHAR(100), perms VARCHAR(255), -- 权限标识,如 'exam:list,exam:create' parent_id VARCHAR(50), status VARCHAR(10), ... ); ``` **示例数据:** ```sql -- 医生菜单权限 INSERT INTO doctor_menu (id, name, perms, status) VALUES ('1', '检查管理', 'exam:list,exam:view', '1'), ('2', '报告管理', 'report:list,report:create', '1'), ('6', '申请医生', 'application:create,application:cancel', '1'); -- 医生角色配置 UPDATE doctors SET doctor_role = '1,2,6' WHERE id = 1; ``` ### 2.2 权限标识命名规范 建议使用 `模块:操作` 的格式: - `exam:list` - 检查列表查询 - `exam:view` - 检查详情查看 - `exam:create` - 创建检查 - `exam:update` - 更新检查 - `exam:delete` - 删除检查 - `report:list` - 报告列表 - `report:create` - 创建报告 ## 3. 登录流程 ### 3.1 登录请求 ```http POST /auth/login Headers: Content-Type: application/json platform: doctor # 可选: doctor/manage,不传则返回所有权限 Body: { "username": "doctor001", "password": "123456" } ``` ### 3.2 登录响应 ```json { "code": 200, "message": "登录成功", "data": { "token": "eyJhbGciOiJIUzUxMiJ9...", // JWT token(包含权限) "userInfo": { ... }, "doctorInfo": { "id": 1, "doctorRole": "1,2,6", "permissions": [ "exam:list", "exam:view", "report:list", "report:create", "application:create", "application:cancel" ] } } } ``` ### 3.3 JWT Token 结构 Token 中包含的 claims: ```json { "username": "doctor001", "userId": 1, "permissions": "exam:list,exam:view,report:list,report:create" // 逗号分隔 } ``` ## 4. 后续请求 ### 4.1 请求携带 Token ```http GET /api/exam/page Headers: Authorization: Bearer eyJhbGciOiJIUzUxMiJ9... Content-Type: application/json ``` ### 4.2 权限验证流程 ``` 1. JwtAuthenticationFilter 拦截请求 ↓ 2. 从 Authorization header 提取 token ↓ 3. JwtUtil.parseUserFromToken(token) 解析 token ↓ 4. 从 token claims 中提取 permissions(逗号分隔字符串) ↓ 5. 转换为 List permissions ↓ 6. 创建 LoginUser(user, permissions) ↓ 7. 设置到 SecurityContext ↓ 8. @PreAuthorize 注解检查权限 ↓ 9. 有权限 → 执行方法;无权限 → 403 Forbidden ``` ## 5. 权限不足处理 ### 5.1 响应示例 ```json { "code": 403, "message": "权限不足", "data": null } ``` ### 5.2 全局异常处理 ```java @RestControllerAdvice public class GlobalExceptionHandler { @ExceptionHandler(AccessDeniedException.class) public RestResult handleAccessDeniedException(AccessDeniedException e) { return RestResult.error(403, "权限不足"); } } ``` ## 6. 优势 ### 6.1 Token 长度优化 - 使用逗号分隔字符串存储权限,而不是 JSON 数组 - 减小 token 大小,提高传输效率 ### 6.2 性能优化 - 权限存储在 token 中,避免每次请求查询数据库 - JwtAuthenticationFilter 直接从 token 解析权限 - UserDetailsService 只在登录时调用一次 ### 6.3 灵活性 - 支持 platform 参数区分医生端/管理端权限 - 支持 doctor 和 manage 权限合并 - 权限自动去重 ## 7. 注意事项 1. **Token 有效期**: 默认 24 小时,可在 `application.yml` 配置 2. **权限变更**: 需要重新登录才能生效(因为权限在 token 中) 3. **Token 刷新**: 如需长期有效,建议实现 refresh token 机制 4. **权限命名**: 统一使用 `模块:操作` 格式,便于管理 ## 8. Spring Security 配置检查 确保 SecurityConfig 已启用方法级别安全: ```java @Configuration @EnableWebSecurity @EnableGlobalMethodSecurity(prePostEnabled = true) // 启用 @PreAuthorize public class SecurityConfig extends WebSecurityConfigurerAdapter { // ... } ```