Spring Boot集成Spring Session单元测试Authentication为空问题求助
我碰到过类似的情况,结合你的代码和错误信息,问题主要出在三个地方:配置冲突、密码编码器缺失、测试环境会话存储的问题。下面一步步来解决:
1. 修复Spring Security的密码编码器问题
首先,Spring Boot 1.5.x对应的Spring Security 4.x中,inMemoryAuthentication默认要求配置密码编码器,否则登录请求会因为密码不匹配而失败,这会直接导致SecurityContext里没有Authentication对象。
修改你的AppConfiguration类,添加密码编码器(注意:生产环境不要用NoOpPasswordEncoder,这里只是为了测试明文密码):
@Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { auth.inMemoryAuthentication() .passwordEncoder(NoOpPasswordEncoder.getInstance()) .withUser("user").password("0000").roles("USER"); }
2. 移除冲突的会话配置
你同时使用了@EnableRedisHttpSession注解和spring.session.store-type=redis配置,在Spring Boot 1.5.x版本中这两个配置会冲突,导致SessionRepository的初始化异常,进而会话无法被正确创建和存储。
直接删除application.properties中的spring.session.store-type=redis这一行即可。
3. 解决测试环境的会话存储问题
单元测试时默认没有Redis服务,Spring Session无法将会话存入Redis,导致SecurityContext无法被持久化,断言时就会出现Authentication为空的情况。这里有两种解决方式:
方式一:使用嵌入式Redis进行测试
添加嵌入式Redis的测试依赖(Maven为例):
<dependency> <groupId>com.github.kstyrc</groupId> <artifactId>embedded-redis</artifactId> <version>0.6</version> <scope>test</scope> </dependency>
然后在测试类中启动和停止嵌入式Redis:
@RunWith(SpringRunner.class) @SpringBootTest @AutoConfigureMockMvc public class DemoRedisDataSessionApplicationTests { @Autowired private MockMvc mockMvc; private RedisServer redisServer; @Before public void setUp() throws IOException { // 启动嵌入式Redis,默认端口6379 redisServer = new RedisServer(6379); redisServer.start(); } @After public void tearDown() { // 测试结束后停止Redis redisServer.stop(); } @Test public void testUserShouldBeAuthenticated() throws Exception { mockMvc.perform(formLogin().user("user").password("0000")) .andExpect(status().is3xxRedirection()) // 明确指定断言用户名,更精准 .andExpect(authenticated().withUsername("user")); } }
方式二:测试时使用内存会话(无需Redis)
如果不想依赖Redis,可以创建测试专用的会话配置,用内存存储会话:
创建测试配置类:
@Configuration @EnableHttpSession public class TestSessionConfiguration { @Bean public SessionRepository<? extends Session> sessionRepository() { // 使用内存版的SessionRepository return new MapSessionRepository(new ConcurrentHashMap<>()); } }
然后在测试类中引入这个配置:
@RunWith(SpringRunner.class) @SpringBootTest @AutoConfigureMockMvc @Import(TestSessionConfiguration.class) public class DemoRedisDataSessionApplicationTests { @Autowired private MockMvc mockMvc; @Test public void testUserShouldBeAuthenticated() throws Exception { mockMvc.perform(formLogin().user("user").password("0000")) .andExpect(status().is3xxRedirection()) .andExpect(authenticated().withUsername("user")); } }
按照上面的步骤修改后,你的测试应该就能通过了,登录后的SecurityContext会正确存储到会话中,断言时就能获取到Authentication对象。
内容的提问来源于stack exchange,提问作者Fabio Maffioletti

