Spring Boot测试中@WithUserDetails无法传入CustomUserDetails问题
问题原因分析
- Security过滤器关闭导致参数解析器缺失:你的测试类中使用了
@AutoConfigureMockMvc(addFilters = false),关闭了Spring Security过滤器链后,Spring Security提供的AuthenticationPrincipalArgumentResolver不会被自动注册。此时Spring MVC无法通过@AuthenticationPrincipal注解正确识别自定义的CustomUserDetails类型,只能通过默认逻辑尝试将Principal转换为父类User,但无法向下转换为子类CustomUserDetails,导致自定义类型参数为null。 - 测试环境下的认证上下文类型转换:虽然自定义
UserDetailsService返回的是CustomUserDetails,但在没有完整Security过滤器链支持的测试环境中,认证上下文的principal可能被默认逻辑转换为标准User对象,而非保留自定义类型。
解决方法
方法1:移除addFilters = false配置
保留Spring Security过滤器链,让AuthenticationPrincipalArgumentResolver自动注册,这样@AuthenticationPrincipal就能正确解析自定义UserDetails类型:
@AutoConfigureMockMvc // 移除addFilters=false配置 class UploadFileTest { // 其余代码保持不变 }
方法2:手动注册参数解析器(需关闭过滤器时使用)
如果必须关闭Security过滤器,可在测试配置中手动添加AuthenticationPrincipalArgumentResolver:
@Configuration public class TestMvcConfig implements WebMvcConfigurer { @Override public void addArgumentResolvers(List<HandlerMethodArgumentResolver> resolvers) { resolvers.add(new AuthenticationPrincipalArgumentResolver()); } }
然后在测试类中引入该配置:
@SpringBootTest( webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, classes = {SpringSecurityTestConfig.class, TestMvcConfig.class} ) @AutoConfigureMockMvc(addFilters = false) class UploadFileTest { // 其余代码保持不变 }
方法3:手动设置SecurityContext替代@WithUserDetails
在测试方法中直接构建认证上下文,确保principal是CustomUserDetails实例:
@Test void doValidUploadFileNoParams() throws Exception { // 从自定义UserDetailsService获取CustomUserDetails CustomUserDetails user = (CustomUserDetails) myUserDetailsService.loadUserByUsername("me@test.com"); // 构建认证对象 Authentication authentication = new UsernamePasswordAuthenticationToken( user, user.getPassword(), user.getAuthorities() ); // 设置到SecurityContext SecurityContextHolder.getContext().setAuthentication(authentication); mockMvc.perform(get("/web/uploadFile")) .andExpect(status().isOk()); }
额外验证步骤
可在控制器方法中临时添加打印,确认principal的实际类型:
@GetMapping("/uploadFile") public String listUploadedFiles(@AuthenticationPrincipal Object principal, Model model) { System.out.println("Principal实际类型: " + principal.getClass().getName()); // 其余业务代码 }
- 若打印结果为
org.springframework.security.core.userdetails.User,说明UserDetailsService返回的实例被转换,需检查测试配置中的InMemoryUserDetailsManager使用逻辑; - 若打印结果为自定义
CustomUserDetails,则问题出在参数解析器的注册上。
内容的提问来源于stack exchange,提问作者BjarneSlamon
相关产品推荐
相关产品推荐

