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>
接下来需要配置实际的RememberMeService
和JdbcTokenRepository
(后者也使用了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机制:
- ✅ 登录时勾选Remember Me
- 关闭浏览器
- 重新打开浏览器并返回同一页面
- 刷新页面——你仍应处于登录状态
未启用Remember Me时,cookie过期后用户会被重定向到登录页面。启用remember me后,用户通过新的令牌/cookie保持登录状态。
你还可以:
- 在浏览器中查看cookie内容
- 在数据库中查看持久化数据(⚠️ 注意:可能需要从嵌入式H2数据库切换到其他数据库才能查看)
7. 总结
本教程展示了如何配置数据库持久化的Remember Me令牌功能,作为对之前标准基于Cookie令牌方案的补充。数据库方案更安全,因为密码细节不会持久化在cookie中——但确实需要稍多的配置工作。
本教程的完整实现可在GitHub项目中找到——这是一个基于Eclipse的项目,可以直接导入运行。