1. 概述

本文将展示如何在Spring Security中设置Remember Me功能——不采用标准的纯cookie方案,而是使用更安全的持久化存储方案

简单来说,Spring可以配置为在浏览器会话之间记住登录信息。这样你登录网站后,下次访问时(即使关闭了浏览器)也能自动登录。

2. 两种"Remember Me"方案对比

Spring提供两种略有不同的实现方案。两者都通过UsernamePasswordAuthenticationFilter的钩子调用RememberMeServices实现。

之前的文章中,我们已经介绍了标准的Remember Me方案(仅使用cookie)。该方案使用名为remember-me的cookie,包含用户名、过期时间和包含密码的MD5哈希值。由于cookie中包含密码哈希,如果cookie被截获,这个方案存在潜在安全风险

考虑到这一点,我们来看看第二种方法——使用PersistentTokenBasedRememberMeServices将持久化登录信息存储在数据库表中。

3. 前提条件——创建数据库表

首先需要创建数据库表来存储登录信息:

create table if not exists persistent_logins ( 
  username varchar_ignorecase(100) not null, 
  series varchar(64) primary key, 
  token varchar(64) not null, 
  last_used timestamp not null 
);

这个表会在启动时自动创建,通过以下XML配置(使用内存H2数据库):

<!-- 启动时创建H2嵌入式数据库表 -->
<jdbc:embedded-database id="dataSource" type="H2">
    <jdbc:script location="classpath:/persisted_logins_create_table.sql"/> 
</jdbc:embedded-database>

为了完整性,这里也给出持久化配置方式:

@Configuration
@EnableTransactionManagement
@PropertySource({ "classpath:persistence-h2.properties" })
public class DatabaseConfig {

    @Autowired private Environment env;

    @Bean
    public DataSource dataSource() {
        DriverManagerDataSource dataSource = new DriverManagerDataSource();
        dataSource.setDriverClassName(env.getProperty("jdbc.driverClassName"));
        dataSource.setUrl(env.getProperty("jdbc.url"));
        dataSource.setUsername(env.getProperty("jdbc.user"));
        dataSource.setPassword(env.getProperty("jdbc.pass"));
        return dataSource;
    }
}

4. Spring Security配置

第一个关键配置是Remember-Me的HTTP配置(注意dataSource属性):

<http use-expressions="true">
    ...
    <remember-me data-source-ref="dataSource" token-validity-seconds="86400"/>
</http> 

接下来需要配置实际的RememberMeServiceJdbcTokenRepository(后者也使用了dataSource):

<!-- 持久化Remember Me服务 -->
<beans:bean id="rememberMeAuthenticationProvider" class=
  "org.springframework.security.web.authentication.rememberme.PersistentTokenBasedRememberMeServices">
    <beans:constructor-arg value="myAppKey" />
    <beans:constructor-arg ref="jdbcTokenRepository" />
    <beans:constructor-arg ref="myUserDetailsService" />
</beans:bean>
 
<!-- 使用数据库表维护持久化登录数据 -->
<beans:bean id="jdbcTokenRepository" 
  class="org.springframework.security.web.authentication.rememberme.JdbcTokenRepositoryImpl"> 
    <beans:property name="createTableOnStartup" value="false" /> 
    <beans:property name="dataSource" ref="dataSource" /> 
</beans:bean>

<!-- 认证管理器(使用与RememberMeService相同的UserDetailsService)--> 
<authentication-manager alias="authenticationManager"> 
    <authentication-provider user-service-ref="myUserDetailsService"/> 
</authentication-manager> 

5. Cookie机制解析

标准TokenBasedRememberMeServices将用户密码的哈希值存储在cookie中。

而本方案——PersistentTokenBasedRememberMeServices使用用户的唯一系列标识符。该标识符标识用户的初始登录,并在该持久会话期间每次自动登录时保持不变。它还包含一个随机令牌,每次用户通过持久化remember-me功能登录时都会重新生成。

这种随机生成的系列和令牌组合会被持久化存储,使得暴力破解攻击变得非常困难。

6. 实践操作指南

要在浏览器中测试remember me机制:

  1. ✅ 登录时勾选Remember Me
  2. 关闭浏览器
  3. 重新打开浏览器并返回同一页面
  4. 刷新页面——你仍应处于登录状态

未启用Remember Me时,cookie过期后用户会被重定向到登录页面。启用remember me后,用户通过新的令牌/cookie保持登录状态。

你还可以:

  • 在浏览器中查看cookie内容
  • 在数据库中查看持久化数据(⚠️ 注意:可能需要从嵌入式H2数据库切换到其他数据库才能查看)

7. 总结

本教程展示了如何配置数据库持久化的Remember Me令牌功能,作为对之前标准基于Cookie令牌方案的补充。数据库方案更安全,因为密码细节不会持久化在cookie中——但确实需要稍多的配置工作。

本教程的完整实现可在GitHub项目中找到——这是一个基于Eclipse的项目,可以直接导入运行。


原始标题:Spring Security – Persistent Remember Me