如何在JUnit5测试代码中向API控制器传递Session?
Spring Boot OAuth2 测试时无法将Session用户传递到控制器
开发环境
- Java 11
- Spring Boot 2.7.2
- Gradle
- JUnit5
- Spring Security & OAuth2
问题描述
业务逻辑正常:OAuth2认证用户发布帖子时,会基于Session中的用户信息注册作者,但JUnit5测试无法将Session传递到API控制器,尝试三种方法均失败:
@WithMockUser(roles = "USER") + MockHttpSession→ Session为null- 仅用
MockHttpSession→ 302重定向到/login @WithSecurityContext→ 403禁止错误
测试代码:
@AutoConfigureMockMvc @SpringBootTest @WithMockUser(roles = "USER") class PostApiControllerTest { @DisplayName("save a post") @Test void save_post() throws Exception { //given User user1 = User.builder() .userRole(Role.USER) .userPicture("picture") .userEmail("email") .username("kim") .build(); userRepository.save(user1); MockHttpSession mockHttpSession = new MockHttpSession(); mockHttpSession.setAttribute("user", new SessionUser(user1)); RequestAddPost post = RequestAddPost.builder() .title("title") .content("content") .categoryName("") .build(); //then mockMvc.perform(post("/write") .contentType(APPLICATION_JSON) .content(objectMapper.writeValueAsString(post)) .session(mockHttpSession)) .andExpect(status().isOk()) .andExpect(jsonPath("$.title").value("title")) .andExpect(jsonPath("$.content").value("content")) .andDo(print()); } }
API控制器:
@RestController @RequiredArgsConstructor public class PostApiController { private final PostService postService; @PostMapping("/write") public ResponsePost writePost(@Validated @RequestBody RequestAddPost requestAddPost, @LoginUser SessionUser user) { return postService.savePost(requestAddPost, user); } }
问题根源
@WithMockUser会自动创建Spring Security的认证上下文和对应Session,覆盖手动设置的MockHttpSession,导致控制器无法获取存入的user属性。- 仅用
MockHttpSession时,请求未通过Spring Security的OAuth2认证拦截,被重定向到登录页。 @WithSecurityContext未正确关联Session中的用户信息,导致权限验证失败。
解决步骤
1. 修改测试代码:移除@WithMockUser,手动添加认证并传递Session
去掉测试类上的@WithMockUser注解,改用mockMvc的with(user(...))方法添加认证信息,同时传递手动创建的MockHttpSession,确保请求既通过Security认证,又能让控制器从Session中拿到用户。
修改后的测试代码:
@AutoConfigureMockMvc @SpringBootTest class PostApiControllerTest { @Autowired private MockMvc mockMvc; @Autowired private ObjectMapper objectMapper; @Autowired private UserRepository userRepository; @DisplayName("保存帖子") @Test void save_post() throws Exception { // given User user1 = User.builder() .userRole(Role.USER) .userPicture("picture") .userEmail("email") .username("kim") .build(); userRepository.save(user1); MockHttpSession mockHttpSession = new MockHttpSession(); mockHttpSession.setAttribute("user", new SessionUser(user1)); RequestAddPost post = RequestAddPost.builder() .title("title") .content("content") .categoryName("") .build(); // when & then mockMvc.perform(post("/write") .contentType(MediaType.APPLICATION_JSON) .content(objectMapper.writeValueAsString(post)) .session(mockHttpSession) // 添加认证信息,匹配用户角色 .with(user(user1.getUsername()).roles(user1.getUserRole().name()))) .andExpect(status().isOk()) .andExpect(jsonPath("$.title").value("title")) .andExpect(jsonPath("$.content").value("content")) .andDo(print()); } }
2. 确保SessionUser实现Serializable接口
Session中的对象必须实现Serializable,否则无法正确存入Session,测试时会丢失用户信息:
public class SessionUser implements Serializable { // 你的属性和方法 }
3. 检查Spring Security配置
确保/write端点允许认证用户访问:
@Configuration @EnableWebSecurity public class SecurityConfig extends WebSecurityConfigurerAdapter { @Override protected void configure(HttpSecurity http) throws Exception { http .authorizeRequests() // 允许认证用户访问/write .antMatchers(HttpMethod.POST, "/write").authenticated() // 其他配置 .and() // OAuth2相关配置 .oauth2Login() // 你的OAuth2配置 ...; } }
原理说明
with(user(...))方法为请求添加Spring Security认证信息,让请求通过OAuth2拦截。- 手动创建的
MockHttpSession被正确传递到控制器,@LoginUser注解的解析器可以从Session中获取user属性。 - 移除
@WithMockUser避免了自动创建的Session覆盖手动设置的Session。
内容的提问来源于stack exchange,提问作者kimsy8979
相关产品推荐
相关产品推荐

