Spring测试:如何测试带有@PreAuthorize自定义权限校验的方法
我经常处理这类Spring Security权限校验的测试场景,下面分享两种实用的测试方式,覆盖集成和单元测试的需求:
1. 集成测试:验证完整权限校验流程
如果想测试端到端的权限逻辑(包括SecurityPermission.hasPermission()的真实业务逻辑),推荐用Spring Boot的集成测试配合Spring Security Test模块:
- 测试类上标注
@SpringBootTest和@AutoConfigureMockMvc,启动完整的Spring上下文并使用MockMvc模拟HTTP请求。 - 用
@WithMockUser或@WithUserDetails模拟不同权限的用户,验证接口的访问控制是否符合预期。
针对你提供的RoleController,示例代码如下:
@SpringBootTest @AutoConfigureMockMvc class RoleControllerIntegrationTest { @Autowired private MockMvc mockMvc; // 测试拥有'role.list'权限的用户可正常访问接口 @Test @WithMockUser(authorities = "role.list") void givenUserWithRoleListPermission_whenAccessingAllRoles_thenReturnsOk() throws Exception { mockMvc.perform(get("/role/allroles") .contentType(MediaType.APPLICATION_JSON)) .andExpect(status().isOk()); } // 测试无'role.list'权限的用户被拒绝访问 @Test @WithMockUser // 默认无任何权限 void givenUserWithoutRoleListPermission_whenAccessingAllRoles_thenReturnsForbidden() throws Exception { mockMvc.perform(get("/role/allroles") .contentType(MediaType.APPLICATION_JSON)) .andExpect(status().isForbidden()); } }
如果你的权限逻辑依赖数据库中的真实用户数据,可以用@WithUserDetails代替@WithMockUser,它会通过你实现的UserDetailsService加载真实用户的权限信息。
2. 单元测试:聚焦注解触发逻辑
如果只想验证**@PreAuthorize注解是否正确调用了SecurityPermission的方法**,而不想测试hasPermission()的内部实现,可以用单元测试+Mock的方式:
- 用
@WebMvcTest只加载控制器层的上下文,避免启动整个应用,提升测试速度。 - 用
@MockBean模拟SecurityPermission和控制器依赖的其他服务(比如RoleService),然后指定hasPermission()的返回值,测试不同场景下的接口响应。
示例代码:
@WebMvcTest(RoleController.class) class RoleControllerUnitTest { @Autowired private MockMvc mockMvc; @MockBean private SecurityPermission securityPermission; @MockBean private RoleService roleService; // 控制器依赖的服务需要mock // 测试当hasPermission返回true时,接口允许访问 @Test void givenHasPermissionReturnsTrue_whenAccessingAllRoles_thenReturnsOk() throws Exception { // 设定mock逻辑:调用hasPermission("role.list")时返回true when(securityPermission.hasPermission("role.list")).thenReturn(true); mockMvc.perform(get("/role/allroles") .contentType(MediaType.APPLICATION_JSON)) .andExpect(status().isOk()); } // 测试当hasPermission返回false时,接口被拒绝 @Test void givenHasPermissionReturnsFalse_whenAccessingAllRoles_thenReturnsForbidden() throws Exception { when(securityPermission.hasPermission("role.list")).thenReturn(false); mockMvc.perform(get("/role/allroles") .contentType(MediaType.APPLICATION_JSON)) .andExpect(status().isForbidden()); } }
3. 非控制器方法的测试(如Service层)
如果你的@PreAuthorize注解用在Service层方法上,可以通过手动设置SecurityContext来模拟用户权限,然后验证方法调用是否抛出AccessDeniedException:
@Test void givenUserWithoutPermission_whenCallingRestrictedMethod_thenThrowsAccessDenied() { // 设置无权限的用户上下文 SecurityContextHolder.getContext().setAuthentication( new UsernamePasswordAuthenticationToken( "testUser", "password", Collections.emptyList() // 空权限集合 ) ); // 调用带权限校验的方法,预期抛出AccessDeniedException assertThrows(AccessDeniedException.class, () -> roleService.someRestrictedMethod()); }
额外提示
- Spring Boot 2.x及以上版本中,Spring Security Test已经包含在
spring-boot-starter-test依赖里,无需额外引入。 - 测试时要覆盖有权限、无权限、权限不匹配等多种场景,确保权限控制逻辑的完整性。
内容的提问来源于stack exchange,提问作者valijon
相关产品推荐
相关产品推荐

