Spring Boot集成测试用Bearer Token报错:SecurityContext无Authentication对象
问题描述
在编写Spring Boot集成测试时,UserController的方法使用了@PreAuthorize注解指定角色权限,但运行测试时抛出以下错误:
jakarta.servlet.ServletException: Request processing failed: org.springframework.security.authentication.AuthenticationCredentialsNotFoundException: An Authentication object was not found in the SecurityContext
相关代码片段如下:
AbstractRestControllerTest
@SpringBootTest @ExtendWith(MockitoExtension.class) @ActiveProfiles(value = "test") @AutoConfigureMockMvc(addFilters = false) public abstract class AbstractRestControllerTest { @Autowired protected MockMvc mockMvc; }
UserController
@RestController @RequestMapping("/api/v1/user") @RequiredArgsConstructor @Validated class UserController { private final UserService userService; private final UserToUserResponseMapper userToUserResponseMapper = UserToUserResponseMapper.initialize(); private final UserToUsersResponseMapper userToUsersResponseMapper = UserToUsersResponseMapper.initialize(); @PostMapping @PreAuthorize("hasAnyAuthority('ADMIN')") public Response<Void> saveUser(@RequestBody @Valid UserSaveRequest saveRequest) { userService.saveUser(saveRequest); return Response.SUCCESS; } }
UserControllerTest
class UserControllerTest extends AbstractRestControllerTest { @MockBean private UserService userService; private static final String BASE_PATH = "/api/v1/user"; @Test void saveUserRequestWithAdminRole_ReturnSuccess() throws Exception { // Given UserSaveRequest mockUserSaveRequest = new UserSaveRequestBuilder() .withFirstName("First Name") .build(); Token mockToken = TokenBuilder.VALID_FOR_ADMIN; Response<Void> mockResponse = ResponseBuilder.SUCCESS; // When Mockito.doNothing().when(userService).saveUser(mockUserSaveRequest); // Then mockMvc.perform(MockMvcRequestBuilders .post(BASE_PATH, mockToken.getAccessToken(), mockUserSaveRequest)) .andDo(print()) .andExpect(status().isOk()) .andExpect(jsonPath("$.time").isNotEmpty()) .andExpect(jsonPath("$.httpStatus").value(mockResponse.getHttpStatus().getReasonPhrase())) .andExpect(jsonPath("$.isSuccess").value(mockResponse.getIsSuccess())) .andExpect(jsonPath("$.response").doesNotExist()); Mockito.verify(userService, Mockito.times(1)).saveUser(Mockito.any()); } }
原因分析
@AutoConfigureMockMvc(addFilters = false)禁用了所有Servlet过滤器,包括Spring Security的认证过滤器。这导致请求头中的Bearer token无法被解析处理,SecurityContext中没有生成对应的Authentication对象。@PreAuthorize注解需要从SecurityContext中获取当前用户的权限信息,由于缺少Authentication对象,直接抛出认证凭证未找到的异常。
修复方案
方案一:使用@WithMockUser快速模拟认证用户(仅测试权限逻辑)
这种方式跳过真实的token认证流程,直接模拟拥有指定权限的用户:
- 在测试方法上添加
@WithMockUser注解,指定用户权限:
@Test @WithMockUser(authorities = "ADMIN") // 模拟拥有ADMIN权限的用户 void saveUserRequestWithAdminRole_ReturnSuccess() throws Exception { // 原测试逻辑保持不变 }
- 保留
AbstractRestControllerTest中的@AutoConfigureMockMvc(addFilters = false),因为我们不需要通过过滤器处理token。
方案二:启用Security过滤器,模拟真实认证流程(测试完整认证+权限逻辑)
如果你需要测试真实的token解析和认证流程,可以按以下步骤修改:
- 修改
AbstractRestControllerTest的注解,移除addFilters = false,让Spring Security过滤器生效:
@SpringBootTest @ExtendWith(MockitoExtension.class) @ActiveProfiles(value = "test") @AutoConfigureMockMvc // 移除addFilters = false public abstract class AbstractRestControllerTest { @Autowired protected MockMvc mockMvc; }
- 使用Spring Security测试工具类模拟JWT认证,在请求中添加权限信息:
mockMvc.perform(MockMvcRequestBuilders .post(BASE_PATH, mockToken.getAccessToken(), mockUserSaveRequest) // 模拟JWT认证并添加ADMIN权限 .with(SecurityMockMvcRequestPostProcessors.jwt() .authorities(new SimpleGrantedAuthority("ADMIN")))) .andDo(print()) .andExpect(status().isOk()) // 后续断言保持不变
- (可选)如果你的项目使用自定义JWT认证,需要在test环境配置文件中添加对应的JWT密钥等参数,确保测试token能被正确解析。
内容的提问来源于stack exchange,提问作者Sercan Noyan Germiyanoğlu
相关产品推荐
相关产品推荐

