在使用 spring-session 的单元测试中,身份验证不应为空

Posted

技术标签:

【中文标题】在使用 spring-session 的单元测试中,身份验证不应为空【英文标题】:Authentication should not be null in unit tests with spring-session 【发布时间】:2018-06-19 17:15:07 【问题描述】:

我有一个 spring boot(版本 1.5.9.RELEASE)应用程序,它使用 spring-session 在 Redis 上存储会话。它还使用 spring-security 对用户进行身份验证。运行应用程序时,成功登录后,安全上下文包含 Authentication 对象。但是在运行单元测试时,我收到此错误消息Authentication should not be null。重现代码如下:

@SpringBootApplication
public class DemoRedisDataSessionApplication 

    @Configuration
    @EnableWebSecurity
    @EnableRedisHttpSession(redisNamespace = "demo-redis-spring-session")
    public static class AppConfiguration extends WebSecurityConfigurerAdapter 

        @Override
        protected void configure(AuthenticationManagerBuilder auth) throws Exception 
            auth.inMemoryAuthentication().withUser("user").password("0000").roles("USER");
        

        @Override
        protected void configure(HttpSecurity http) throws Exception 
            http.formLogin().and()
                    .authorizeRequests().anyRequest().fullyAuthenticated();
        

    

    @RestController
    public static class AppController 

        @GetMapping("/secured")
        public String secured() 
            return "secured";
        

    

    public static void main(String[] args) 
        SpringApplication.run(DemoRedisDataSessionApplication.class, args);
    


这里是 application.properties

spring.session.store-type=redis

这是失败的测试

@RunWith(SpringRunner.class)
@SpringBootTest
@AutoConfigureMockMvc
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());
    


失败测试的错误信息:

java.lang.AssertionError: Authentication should not be null

    at org.springframework.test.util.AssertionErrors.fail(AssertionErrors.java:35)
    at org.springframework.test.util.AssertionErrors.assertTrue(AssertionErrors.java:65)
    at org.springframework.security.test.web.servlet.response.SecurityMockMvcResultMatchers$AuthenticatedMatcher.match(SecurityMockMvcResultMatchers.java:98)

尤其是 HttpSessionSecurityContextRepository 第 110 行中的会话似乎为空,但我不明白为什么。

我希望用户通过身份验证并在成功登录后填充SecurityContext。你知道如何解决这个问题吗?

【问题讨论】:

【参考方案1】:

更新:

首先,您需要指示您的身份验证提供程序(在您的情况下,它是默认的DaoAuthenticationProvider)返回什么样的Authentication 对象。例如,您可以在自定义的WebSecurityConfigurerAdapter 中将httpBasic() 添加到您的configure(HttpSecurity http) 方法中。本质上,httpBasic() 会将您的用户名和密码转换为 UsernamePasswordAuthenticationToken 对象,以便您的 DaoAuthenticationProvider 可以使用它进行身份验证。

另外,你需要permitAll为你的登录url。

    @Override
    protected void configure(HttpSecurity http) throws Exception 
        http.formLogin().and()
                .authorizeRequests()
                .antMatchers("/login/**").permitAll()
                .anyRequest().fullyAuthenticated()
                .and().httpBasic();
    

关于单元测试,问题是由于您没有将 spring 安全性连接到您的 mockMvc 对象中。由于您实际上是 spring-boot,我会给您一个带有 spring-boot-test 的示例解决方案:

@RunWith(SpringRunner.class)
@SpringBootTest
@WebAppConfiguration
public class DemoRedisDataSessionApplicationTests 

@Autowired
WebApplicationContext wac;

private MockMvc mockMvc;

@Before
public void setUp() 

    mockMvc = MockMvcBuilders.webAppContextSetup(wac)
            .apply(springSecurity())
            .build();


@Test
public void testUserShouldBeAuthenticated() throws Exception 
    mockMvc.perform(formLogin().user("user").password("0000"))
            .andExpect(status().is3xxRedirection())
            .andExpect(authenticated());


重点说明:以上代码中的springSecurity()来自import static org.springframework.security.test.web.servlet.setup.SecurityMockMvcConfigurers.springSecurity

【讨论】:

谢谢,但不幸的是,即使使用您提出的解决方案,我也会遇到同样的错误...我认为问题在于 spring-session 库与单元测试的集成效果不佳。 不应该与 spring-session 相关的这个特定错误...您可以尝试在 AppConfiguration 的 protected void configure(HttpSecurity http) throws Exception 方法中添加 httpBasic() 吗? 法比奥,我的意思是:@Override protected void configure(HttpSecurity http) throws Exception http.formLogin().and() .authorizeRequests().anyRequest().fullyAuthenticated().and().httpBasic(); 使用httpBasic() 可以正常使用,但不幸的是我们不能使用它...我们需要坚持使用formLogin() 法比奥,这两个概念并不冲突。 formLogin() 是为您的应用程序提供一个隐式登录页面,而httpBasic() 是为您的身份验证提供者提供一种对用户进行身份验证的方法。从本质上讲,httpBasic() 会将您的用户名和密码转换为 UsernamePasswordAuthenticationToken 对象,以便您的 DaoAuthenticationProvider 可以使用它进行身份验证。

以上是关于在使用 spring-session 的单元测试中,身份验证不应为空的主要内容,如果未能解决你的问题,请参考以下文章

spring-session揭秘

初试spring-session

如何使用 JDBC 在 spring-session 中初始化模式

Spring-session整合到Redis

使用 Spring-Session 时更新 Session 中的 Principal

spring-session揭秘续篇