You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Spring测试:如何测试带有@PreAuthorize自定义权限校验的方法

测试带有@PreAuthorize("@SecurityPermission.hasPermission('somepermission')")注解的方法

我经常处理这类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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.21 04:22:08