测试标注@PreAuthorize的Spring Boot控制器遇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
相关产品推荐
相关产品推荐

