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

测试标注@PreAuthorize的Spring Boot控制器遇401,该如何处理?

解决@PreAuthorize控制器方法测试返回401的问题

我看了你的代码,发现有两个核心问题导致测试返回401,咱们一步步解决:

1. 修复请求方法与路径的不匹配

你的控制器方法用的是@PostMapping(path="/users/{id}"),但测试里却用了MockMvcRequestBuilders.get("/user/1")——请求方法不对(POST vs GET),路径也少了个s(/users vs /user),这首先就会导致请求无法匹配到对应的控制器方法,加上Security的拦截,自然返回401。

先把测试里的请求改成正确的POST请求和路径:

mockMvc.perform(MockMvcRequestBuilders.post("/users/1"))
        // ... 其余断言保持不变

2. 让@WebMvcTest支持方法级安全注解@PreAuthorize

@WebMvcTest的设计是只加载控制器相关的Bean,默认不会启用Spring Security的方法级安全(也就是@PreAuthorize这类注解的处理逻辑)。要让它生效,你有两个可选方案:

方案一:在测试类中启用方法级安全

在你的测试类上添加@EnableGlobalMethodSecurity(prePostEnabled = true),同时配置方法安全的处理器Bean。修改后的测试类如下:

@RunWith(SpringRunner.class)
@WebMvcTest(ClientController.class)
@EnableGlobalMethodSecurity(prePostEnabled = true) // 启用方法级安全
public class ClientControllerTest {

    @Autowired
    private MockMvc mockMvc;

    // 配置方法安全表达式处理器
    @Bean
    public MethodSecurityExpressionHandler methodSecurityExpressionHandler() {
        return new DefaultMethodSecurityExpressionHandler();
    }

    @WithMockUser(authorities={"CAN_READ"})
    @Test
    public void should_get_user_by_id() throws Exception {
        // 修正请求方法和路径
        mockMvc.perform(MockMvcRequestBuilders.post("/users/1"))
                .andExpect(MockMvcResultMatchers.status().isOk())
                .andExpect(MockMvcResultMatchers.header().string(HttpHeaders.CONTENT_TYPE, "application/json"))
                .andExpect(MockMvcResultMatchers.jsonPath("$.name").value("johnson"));
    }
}

方案二:改用@SpringBootTest + @AutoConfigureMockMvc

如果你希望加载完整的Spring应用上下文(包括你的ResourceServerConfig配置),可以放弃@WebMvcTest,改用@SpringBootTest结合@AutoConfigureMockMvc,这样会自动加载所有配置,包括方法级安全和资源服务器的规则:

@RunWith(SpringRunner.class)
@SpringBootTest
@AutoConfigureMockMvc
public class ClientControllerTest {

    @Autowired
    private MockMvc mockMvc;

    @WithMockUser(authorities={"CAN_READ"})
    @Test
    public void should_get_user_by_id() throws Exception {
        // 修正请求方法和路径
        mockMvc.perform(MockMvcRequestBuilders.post("/users/1"))
                .andExpect(MockMvcResultMatchers.status().isOk())
                .andExpect(MockMvcResultMatchers.header().string(HttpHeaders.CONTENT_TYPE, "application/json"))
                .andExpect(MockMvcResultMatchers.jsonPath("$.name").value("johnson"));
    }
}

额外注意点

  • 确认@WithMockUser(authorities={"CAN_READ"})里的权限和控制器@PreAuthorize("hasAnyAuthority('CAN_READ')")的权限完全匹配,大小写也要一致。
  • 如果用方案二,你的ResourceServerConfig会被加载,测试时会应用里面的Security规则,比如/public/**允许匿名访问,其他请求需要认证,这和你的测试场景是匹配的。

内容的提问来源于stack exchange,提问作者Moses Besong

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.06 07:49:09