Spring Boot微服务JUnit测试:含@PreAuthorize的方法角色配置问题
背景
我在订单控制器的方法中添加了@PreAuthorize("hasAuthority('ROLE_ADMIN')")和@PreAuthorize("hasAuthority('ROLE_USER')")注解,认证服务已完成用户、管理员角色的登录方法定义,现在需要修改控制器测试逻辑。
给placeOrder方法添加@PreAuthorize("hasAuthority('ROLE_USER')")后,使用JWT令牌的测试方法可以正常通过,但尝试用@WithMockUser(roles="USER")和@WithMockUser(roles="ADMIN")模拟角色时,始终返回403 Forbidden错误。此前添加过WebSecurityConfig配置类但未解决问题,已暂时移除。
相关代码
订单控制器方法
@PreAuthorize("hasAuthority('ROLE_USER')") @PostMapping("/placeorder") public ResponseEntity<Long> placeOrder(@RequestBody OrderRequest orderRequest) { log.info("OrderController | placeOrder is called"); log.info("OrderController | placeOrder | orderRequest: {}", orderRequest.toString()); long orderId = orderService.placeOrder(orderRequest); log.info("Order Id: {}", orderId); return new ResponseEntity<>(orderId, HttpStatus.OK); }
可正常运行的测试方法(使用JWT)
@Test @DisplayName("Place Order -- Success Scenario") void test_When_placeOrder_DoPayment_Success() throws Exception { OrderRequest orderRequest = getMockOrderRequest(); String jwt = getJWTTokenForRoleUser(); MvcResult mvcResult = mockMvc.perform(MockMvcRequestBuilders.post("/order/placeorder") .contentType(MediaType.APPLICATION_JSON_VALUE) .header("Authorization", "Bearer " + jwt) .content(objectMapper.writeValueAsString(orderRequest))) .andExpect(MockMvcResultMatchers.status().isOk()) .andReturn(); String orderId = mvcResult.getResponse().getContentAsString(); Optional<Order> order = orderRepository.findById(Long.valueOf(orderId)); assertTrue(order.isPresent()); Order o = order.get(); assertEquals(Long.parseLong(orderId), o.getId()); assertEquals("PLACED", o.getOrderStatus()); assertEquals(orderRequest.getTotalAmount(), o.getAmount()); assertEquals(orderRequest.getQuantity(), o.getQuantity()); }
项目依赖
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId> </dependency> <dependency> <groupId>org.springframework.security</groupId> <artifactId>spring-security-test</artifactId> <scope>test</scope> </dependency>
已移除的WebSecurityConfig配置类
@Configuration @EnableWebSecurity @EnableGlobalMethodSecurity(prePostEnabled = true) public class WebSecurityConfig { @Bean public SecurityFilterChain securityWebFilterChain(HttpSecurity http) throws Exception { http .authorizeRequests( authorizeRequest -> authorizeRequest .anyRequest() .authenticated()); return http.build(); } }
解决方案
核心原因
- 移除WebSecurityConfig后,
@EnableGlobalMethodSecurity(prePostEnabled = true)未生效,导致@PreAuthorize注解不被Spring Security处理; - 测试时未禁用CSRF防护,POST请求被Spring Security拦截;
@WithMockUser的使用方式与权限校验逻辑不匹配(虽表面匹配,但需确保配置生效)。
具体修复步骤
1. 恢复并修正WebSecurityConfig配置类
必须启用方法级安全才能让@PreAuthorize生效,同时测试环境需禁用CSRF:
@Configuration @EnableWebSecurity @EnableMethodSecurity(prePostEnabled = true) // Spring Boot 2.7+/3.x推荐用这个替代@EnableGlobalMethodSecurity public class WebSecurityConfig { @Bean public SecurityFilterChain securityWebFilterChain(HttpSecurity http) throws Exception { http .authorizeHttpRequests(auth -> auth .anyRequest().authenticated() ) .csrf(csrf -> csrf.disable()); // 测试环境禁用CSRF,生产环境按需配置 return http.build(); } }
2. 正确使用@WithMockUser
两种写法均匹配hasAuthority('ROLE_USER')的校验逻辑:
// 写法1:通过roles参数自动生成ROLE_USER权限 @Test @DisplayName("Place Order -- Success with Mock User") @WithMockUser(roles = "USER") void test_When_placeOrder_WithMockUser_Success() throws Exception { mockMvc.perform(MockMvcRequestBuilders.post("/order/placeorder") .contentType(MediaType.APPLICATION_JSON_VALUE) .content(objectMapper.writeValueAsString(getMockOrderRequest()))) .andExpect(MockMvcResultMatchers.status().isOk()); } // 写法2:直接指定权限字符串,与控制器注解完全匹配 @Test @DisplayName("Place Order -- Success with Mock Authority") @WithMockUser(authorities = "ROLE_USER") void test_When_placeOrder_WithMockAuthority_Success() throws Exception { // 测试逻辑同上 }
3. 确保测试类正确加载上下文
测试类需添加@SpringBootTest和@AutoConfigureMockMvc,保证Spring Security配置与MockMvc初始化:
@SpringBootTest @AutoConfigureMockMvc class OrderControllerTest { @Autowired private MockMvc mockMvc; // 其他依赖注入与测试方法... }
内容的提问来源于stack exchange,提问作者Sercan Noyan Germiyanoğlu
相关产品推荐
相关产品推荐

