测试Spring Security时@AuthenticationPrincipal始终为null如何解决
问题根因
@WithMockUser 注解默认生成的身份凭证中,principal 字段是字符串类型的用户名,和你控制器中需要注入的自定义 User 类型不匹配,Spring MVC 参数解析器无法完成类型转换,因此最终注入的参数为 null。
可行解决方案
方案1:使用SecurityMockMvcRequestPostProcessors传入自定义User对象
该方案最直接,不需要额外加类注解,直接在构造请求时传入你自己实例化的User对象即可:
@Test public void test() throws Exception { // 提前构造你需要的User实例,按需填充属性 User mockUser = new User(); mockUser.setUsername("test"); mockUser.setId(1L); this.mockMvc.perform(get("/account") .with(SecurityMockMvcRequestPostProcessors.user(mockUser))) .andDo(print()); }
方案2:手动设置SecurityContext
在测试方法执行前,手动构造身份凭证存入安全上下文,适合需要复用身份信息的多请求测试场景:
@Test public void test() throws Exception { User mockUser = new User(); // 填充自定义属性 mockUser.setUsername("test"); UsernamePasswordAuthenticationToken auth = new UsernamePasswordAuthenticationToken(mockUser, null, AuthorityUtils.createAuthorityList("ROLE_USER")); SecurityContextHolder.getContext().setAuthentication(auth); this.mockMvc.perform(get("/account")).andDo(print()); }
方案3:配合@WithUserDetails使用
如果你的项目中已经实现了UserDetailsService接口,且该接口返回的就是自定义User类型,可采用该方案:
@WebMvcTest public class GazerControllerTest { @MockBean UserService userService; @MockBean ClientService clientService; // 新增Mock UserDetailsService @MockBean UserDetailsService userDetailsService; @Autowired MockMvc mockMvc; @Test @WithUserDetails("testUser") public void test() throws Exception { User mockUser = new User(); mockUser.setUsername("testUser"); // Mock查询逻辑返回自定义User对象 when(userDetailsService.loadUserByUsername(eq("testUser"))).thenReturn(mockUser); this.mockMvc.perform(get("/account")).andDo(print()); } }
注意:请确认你的自定义
User类实现了Spring Security的UserDetails接口,否则需要自行扩展AuthenticationPrincipalArgumentResolver完成自定义类型的解析,大多数场景下实现UserDetails接口是成本最低的方案。
内容的提问来源于stack exchange,提问作者Randall1820
相关产品推荐
相关产品推荐

