Spring 安全 RememberMe 在 Safari 中不工作

Spring Security RememberMe Not Working in Safari

我已经成功配置了一个 Spring 应用程序,具有 Spring 安全性和 Persistent RememberMe 功能。但是,以下步骤在 Safari 7.1.2 中会产生错误:

  1. 登录并记住我(已确认在数据库中创建令牌)。
  2. 从浏览器中手动删除 JSESSIONID cookie 以模拟会话过期。
  3. 刷新浏览器。

产生的错误是:

org.springframework.security.web.authentication.rememberme.CookieTheftException: Invalid remember-me token (Series/token) mismatch. Implies previous cookie theft attack.

在 FireFox 31.3.0 中执行这些完全相同的步骤后,用户将按预期再次成功登录。

以下是应用程序安全的 Java 配置:

@Configuration
@EnableWebMvcSecurity
@ComponentScan(basePackages={"com.example.app.config"})
public class SecurityConfig extends WebSecurityConfigurerAdapter{

@Autowired
private DataSource dataSource;

@Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {

    auth.jdbcAuthentication()
        .dataSource(dataSource);

}

@Override
public void configure(HttpSecurity http) throws Exception {
    http.authorizeRequests()
        .antMatchers("/new").access("hasRole('USER')")
        .antMatchers("/call/**").access("hasRole('USER')")
        .antMatchers("/contacts/**").access("hasRole('USER')")
        .antMatchers("/").access("hasRole('USER')")
        .antMatchers("/resources/css/**").permitAll()
        .antMatchers("/resources/js/**").permitAll()
        .and()
            .formLogin()
            .loginPage("/signin")
            .loginProcessingUrl("/j_spring_security_check")
            .usernameParameter("username")
            .passwordParameter("password")
            .permitAll();

    http.rememberMe()
        .key("notasecret")
        .rememberMeServices(rememberMeServices())
        .userDetailsService(userDetailsService());
}

@Bean 
public JdbcDaoImpl userDetailsService() {
    JdbcDaoImpl userDetailsService = new JdbcDaoImpl();
    userDetailsService.setDataSource(dataSource);
    return userDetailsService;
}

@Bean
public PersistentTokenBasedRememberMeServices rememberMeServices() {
    PersistentTokenBasedRememberMeServices services = new PersistentTokenBasedRememberMeServices("notasecret", userDetailsService(), tokenRepository());
    services.setTokenValiditySeconds(43200);
    return services;
}

@Bean
public JdbcTokenRepositoryImpl tokenRepository() {
    JdbcTokenRepositoryImpl repository = new JdbcTokenRepositoryImpl();
    repository.setDataSource(dataSource);
    return repository;
}

以下是 Safari 的调试跟踪中发生的情况:

DEBUG: org.springframework.security.web.FilterChainProxy - / at position 1 of 13 in additional filter chain; firing Filter: 'WebAsyncManagerIntegrationFilter'
DEBUG: org.springframework.security.web.FilterChainProxy - / at position 2 of 13 in additional filter chain; firing Filter: 'SecurityContextPersistenceFilter'
DEBUG: org.springframework.security.web.context.HttpSessionSecurityContextRepository - No HttpSession currently exists
DEBUG: org.springframework.security.web.context.HttpSessionSecurityContextRepository - No SecurityContext was available from the HttpSession: null. A new one will be created.
DEBUG: org.springframework.security.web.FilterChainProxy - / at position 3 of 13 in additional filter chain; firing Filter: 'HeaderWriterFilter'
DEBUG: org.springframework.security.web.header.writers.HstsHeaderWriter - Not injecting HSTS header since it did not match the requestMatcher org.springframework.security.web.header.writers.HstsHeaderWriter$SecureRequestMatcher@69d3d174
DEBUG: org.springframework.security.web.FilterChainProxy - / at position 4 of 13 in additional filter chain; firing Filter: 'CsrfFilter'
DEBUG: org.springframework.security.web.FilterChainProxy - / at position 5 of 13 in additional filter chain; firing Filter: 'LogoutFilter'
DEBUG: org.springframework.security.web.util.matcher.AntPathRequestMatcher - Request 'GET /' doesn't match 'POST /logout
DEBUG: org.springframework.security.web.FilterChainProxy - / at position 6 of 13 in additional filter chain; firing Filter: 'UsernamePasswordAuthenticationFilter'
DEBUG: org.springframework.security.web.util.matcher.AntPathRequestMatcher - Request 'GET /' doesn't match 'POST /j_spring_security_check
DEBUG: org.springframework.security.web.FilterChainProxy - / at position 7 of 13 in additional filter chain; firing Filter: 'RequestCacheAwareFilter'
DEBUG: org.springframework.security.web.FilterChainProxy - / at position 8 of 13 in additional filter chain; firing Filter: 'SecurityContextHolderAwareRequestFilter'
DEBUG: org.springframework.security.web.FilterChainProxy - / at position 9 of 13 in additional filter chain; firing Filter: 'RememberMeAuthenticationFilter'
DEBUG: org.springframework.security.web.authentication.rememberme.PersistentTokenBasedRememberMeServices - Remember-me cookie detected
DEBUG: org.springframework.security.web.authentication.rememberme.PersistentTokenBasedRememberMeServices - Cancelling cookie
DEBUG: org.springframework.security.web.context.HttpSessionSecurityContextRepository - SecurityContext is empty or contents are anonymous - context will not be stored in HttpSession.
DEBUG: org.springframework.security.web.context.SecurityContextPersistenceFilter - SecurityContextHolder now cleared, as request processing completed
Jan 27, 2015 11:54:17 AM org.apache.catalina.core.StandardWrapperValve invoke
SEVERE: Servlet.service() for servlet [appServlet] in context with path [/ocl] threw exception
org.springframework.security.web.authentication.rememberme.CookieTheftException: Invalid remember-me token (Series/token) mismatch. Implies previous cookie theft attack.

我的理论是日志中指出 "Cancelling cookie" 的那一点是问题所在。但是,我不知道为什么会这样。

如果有人 运行 遇到这个问题,或者上述配置是否有问题或遗漏,请告诉我。

我从一个简单的纯安全应用程序开始,以消除所有其他复杂性,并能够让记住我的功能正常工作。我在我的测试应用程序和主应用程序之间各自的 POM.xml 文件中达到了峰值,并从记住我不工作的应用程序中删除了以下依赖项:

<dependency>
    <groupId>org.springframework.security</groupId>
    <artifactId>spring-security-web</artifactId>
    <version>3.2.5.RELEASE</version>
</dependency>

这肯定与 spring-security-core 有冲突,因为在我删除依赖项后一切正常。