在 Spring Security 中,权限控制是保障系统安全的重要环节。本文将详细解析三个核心安全注解:@RequiresAuthentication@RequiresPermissions@RequiresRoles 的异同点,并提供代码示例。

一、注解概述

@RequiresAuthentication

  • 作用:要求用户必须已认证(登录)才能访问该方法或类
  • 级别:最基本的权限控制
  • 特点:不关心用户的具体权限或角色,只验证是否登录

@RequiresPermissions

  • 作用:要求用户必须拥有指定的权限才能访问
  • 级别:细粒度的权限控制
  • 特点:基于权限字符串进行验证,灵活性高

@RequiresRoles

  • 作用:要求用户必须属于指定的角色才能访问
  • 级别:粗粒度的权限控制
  • 特点:基于角色进行验证,通常角色会关联一组权限
二、异同点对比
特性RequiresAuthenticationRequiresPermissionsRequiresRoles
验证内容是否认证特定权限特定角色
粒度最粗最细中等
应用场景所有需登录的接口具体操作权限控制角色分类控制
是否可组合使用
默认错误处理抛出AuthorizationException抛出AuthorizationException抛出AuthorizationException
三、代码示例

1.@RequiresAuthentication 示例

import org.apache.shiro.authz.annotation.RequiresAuthentication;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class AuthController {
    
    @GetMapping("/profile")
    @RequiresAuthentication
    public String getUserProfile() {
        return "用户个人资料(需登录访问)";
    }
}

2.@RequiresPermissions 示例

import org.apache.shiro.authz.annotation.RequiresPermissions;
import org.springframework.web.bind.annotation.*;

@RestController
@RequestMapping("/document")
public class DocumentController {
    
    @GetMapping("/{id}")
    @RequiresPermissions("document:read")
    public String readDocument(@PathVariable String id) {
        return "读取文档ID: " + id;
    }
    
    @PostMapping
    @RequiresPermissions("document:create")
    public String createDocument() {
        return "创建新文档";
    }
}

3.@RequiresRoles 示例

import org.apache.shiro.authz.annotation.RequiresRoles;
import org.springframework.web.bind.annotation.*;

@RestController
@RequestMapping("/admin")
public class AdminController {
    
    @GetMapping("/dashboard")
    @RequiresRoles("admin")
    public String adminDashboard() {
        return "管理员仪表盘";
    }
    
    @GetMapping("/audit")
    @RequiresRoles({"admin", "auditor"})
    public String auditLogs() {
        return "审计日志(管理员或审计员可访问)";
    }
}

4.组合使用

@RestController
@RequestMapping("/system")
public class SystemController {
    
    @GetMapping("/settings")
    @RequiresAuthentication
    @RequiresRoles("admin")
    @RequiresPermissions("system:configure")
    public String systemSettings() {
        return "系统设置(需登录+管理员角色+配置权限)";
    }
}
四、高级用法与注意事项
  1. 逻辑关系
    • 多个注解同时使用时是”与”关系
    • 同一注解多个值是”或”关系(如@RequiresRoles({"admin", "superadmin"})
  2. 自定义错误处理
@ControllerAdvice
public class GlobalExceptionHandler {
    
    @ExceptionHandler(AuthorizationException.class)
    public ResponseEntity<String> handleAuthException(AuthorizationException e) {
        return ResponseEntity.status(HttpStatus.FORBIDDEN)
                .body("访问被拒绝: " + e.getMessage());
    }
}

3.性能考虑

权限检查会在每次请求时执行

对于频繁访问的接口,考虑缓存权限信息

4.测试建议

@SpringBootTest
@AutoConfigureMockMvc
class SecurityAnnotationTest {
    
    @Autowired
    private MockMvc mockMvc;
    
    @Test
    @WithMockUser
    void testRequiresAuthentication() throws Exception {
        mockMvc.perform(get("/profile"))
               .andExpect(status().isOk());
    }
    
    @Test
    @WithMockUser(roles = "admin")
    void testRequiresRoles() throws Exception {
        mockMvc.perform(get("/admin/dashboard"))
               .andExpect(status().isOk());
    }
}
五、总结
  • 选择依据:
    • 只需验证登录状态 → @RequiresAuthentication
    • 基于业务功能控制 → @RequiresPermissions
    • 基于用户角色分类 → @RequiresRoles
  • 最佳实践:
    • 优先使用@RequiresPermissions实现细粒度控制
    • 角色用于用户分类,权限用于功能控制
    • 复杂场景可以组合使用多个注解