You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Spring Boot测试中@WithUserDetails无法传入CustomUserDetails问题

问题原因分析
  1. Security过滤器关闭导致参数解析器缺失:你的测试类中使用了@AutoConfigureMockMvc(addFilters = false),关闭了Spring Security过滤器链后,Spring Security提供的AuthenticationPrincipalArgumentResolver不会被自动注册。此时Spring MVC无法通过@AuthenticationPrincipal注解正确识别自定义的CustomUserDetails类型,只能通过默认逻辑尝试将Principal转换为父类User,但无法向下转换为子类CustomUserDetails,导致自定义类型参数为null。
  2. 测试环境下的认证上下文类型转换:虽然自定义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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.17 18:24:50