Spring Boot测试@WithMockUser时@AuthenticationPrincipal注入为null
Controller层MockMvc测试@AuthenticationPrincipal入参为null问题
问题现象
项目升级至Java 17、Spring Boot 2.5.5、Spring Cloud 2020.0.5版本后,Controller层单元测试出现异常:
- Controller方法中标记
@AuthenticationPrincipal注解的Authentication类型入参值为null - 方法内部直接调用
SecurityContextHolder.getContext().getAuthentication()可正常获取@WithMockUser生成的模拟认证对象
相关代码片段
测试基类配置
@SpringBootTest @WithMockUser(username = "test_user",authorities = "Configured_allacess_authority") public abstract class BaseControllerTest extends DatabaseIT { protected static long counter; protected MockMvc mockMvc; @Autowired private WebApplicationContext webApplicationContext; @Autowired protected ObjectMapper objectMapper; protected MockRestServiceServer restServiceServer; @Autowired RestTemplate restTemplate; @BeforeEach protected void setup() { mockMvc = MockMvcBuilders .webAppContextSetup(webApplicationContext) .build(); restServiceServer = MockRestServiceServer.createServer(restTemplate); } }
测试执行逻辑
mockMvc.perform(request) .andExpect(status().isOk()) .andExpect(content().contentType(MediaType.APPLICATION_JSON)) .andExpect(content().json(s));
异常Controller方法
public ResponseEntity<?> getSomething ( @Parameter(description = "ID") final String id, @ApiIgnore @AuthenticationPrincipal Authentication user){ // 业务逻辑 }
根因
手动通过WebApplicationContext构建MockMvc实例时,未显式应用Spring Security测试模块的配置,导致@AuthenticationPrincipal对应的参数解析器AuthenticationPrincipalArgumentResolver未被注册到MockMvc的参数解析链路中:
- 直接读取
SecurityContextHolder是直接获取线程上下文绑定的安全上下文值,不经过MVC参数解析流程,因此可以正常拿到模拟用户信息 - 注解标记的Controller入参需要经过MVC参数解析器链完成注入,缺少对应解析器时就会出现注入值为null的情况
- 升级到指定版本后,Spring Security测试模块不再默认给手动构建的MockMvc实例隐式注册安全相关组件,必须显式声明配置。
修复方法
- 确认测试模块已引入
spring-security-test依赖 - 修改测试基类的MockMvc构建逻辑,显式应用Spring Security测试配置:
// 导入静态配置类 import static org.springframework.security.test.web.servlet.setup.SecurityMockMvcConfigurers.springSecurity; @BeforeEach protected void setup() { mockMvc = MockMvcBuilders .webAppContextSetup(webApplicationContext) // 新增该行,加载Spring Security测试过滤器、参数解析器等组件 .apply(springSecurity()) .build(); restServiceServer = MockRestServiceServer.createServer(restTemplate); }
修改后重新执行测试,@AuthenticationPrincipal标记的入参即可正常注入模拟认证对象。
内容的提问来源于stack exchange,提问作者Iulii Turkin
相关产品推荐
相关产品推荐

