spring security中记住我的实现逻辑
·
一、Remember-Me 的整体工作流程
- 用户登录:用户成功登录时,如果勾选了“记住我”,
Spring Security会生成一个Remember-Me Cookie并写入用户浏览器。 - 后续请求:当用户再次访问应用时,即使没有
Session(Session 可能已过期),浏览器也会自动携带这个Cookie。 - 过滤器拦截:
Spring Security的RememberMeAuthenticationFilter会拦截该请求,从Cookie中解析出令牌信息。 - 身份验证:过滤器根据令牌信息(可能是哈希值或系列标识符)去验证其有效性,并加载对应的用户信息
(UserDetails)。 - 创建认证对象:验证成功后,创建一个
RememberMeAuthenticationToken对象,并将其设置到SecurityContextHolder中,表示用户已被“记住”并自动登录。 - 访问资源:用户可以正常访问受保护的资源。
二、实现方式一:简单哈希(Simple Hash)
1.原理
这种方式不需要服务器端存储。它通过以下信息生成一个哈希值作为令牌:用户名 、密码(或密码的哈希值)、过期时间、一个私有密钥(在应用中配置)。
生成的令牌格式通常为:base64(username + ":" + expirationTime + ":" + md5Hex(username + ":" + expirationTime + ":" + password + ":" + key))。
2.配置方式
在 Spring Security 配置类中启用:
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.rememberMe(rememberMe -> rememberMe
.key("uniqueAndSecretKey") // 设置私有密钥,必须足够复杂且保密
.rememberMeCookieName("my-remember-me-cookie") // 自定义Cookie名称
.rememberMeParameter("remember-me") // 自定义登录表单中的参数名
.tokenValiditySeconds(3600 * 24 * 7) // 设置令牌有效期(秒),默认两周
.useSecureCookie(true) // 仅在HTTPS下传输Cookie
);
return http.build();
}
3. 优缺点
- 优点:实现简单,无需额外存储。
- 缺点:
- 安全性较低:令牌中包含了用户名和过期时间(虽然是Base64编码,但可轻易解码)。如果密钥泄露,攻击者可以伪造令牌。
- 无法撤销:一旦令牌生成,在过期前一直有效。如果用户修改了密码,旧的令牌依然有效,存在安全风险。
- 依赖密码:如果用户修改了密码,所有之前生成的记住我令牌都会失效(因为密码变了,哈希值对不上),这既是缺点也是一种安全特性。
三、实现方式二:持久化令牌(Persistent Token)
这种方式更安全、更灵活,是推荐的生产环境使用方式。它需要将令牌信息存储在数据库中。
1. 核心概念
它使用一个系列(Series) 的概念。一个系列对应一个用户的一组令牌。
- 系列标识符(Series Identifier):一个随机生成的、长期有效的字符串,用于标识一组令牌。它被存储在 Cookie 中。
- 令牌值(Token Value):一个随机生成的、短期有效的字符串,用于实际验证身份。它存储在数据库中,并且每次请求后都会更换(轮换),以防止重放攻击。
数据库表结构
需要创建一张表来存储令牌,Spring Security 提供了标准的建表语句:
create table persistent_logins (
username varchar(64) not null,
series varchar(64) primary key,
token varchar(64) not null,
last_used timestamp not null
);
sername: 用户名series: 系列标识符(Cookie中存储的值)token: 当前有效的令牌值last_used: 最后一次使用的时间
3. 工作流程
- 生成令牌:用户登录并选择“记住我”时,系统生成一个随机的
series和token,将(username, series, token, last_used)存入数据库,并将series和token组合成Cookie发送给浏览器(例如:base64(series + ":" + token)。 - 验证令牌:
- 浏览器携带
Cookie访问。 - 过滤器解析出
series和token。 - 根据
series去数据库查询记录。 - 比较数据库中的
token和Cookie中的token是否匹配。 - 如果匹配,验证通过,并生成一个新的随机
token更新到数据库和Cookie中(令牌轮换),更新last_used时间。 - 如果不匹配,说明令牌可能已被盗用,验证失败,并删除该系列的所有记录(撤销登录)。
- 浏览器携带
- 撤销登录:当用户主动登出、修改密码或管理员强制下线时,只需根据 username 或 series 删除数据库中的记录即可,所有记住我的会话立即失效。
4. 配置方式
- 添加依赖(如使用JDBC)
- 配置数据源和
JdbcTemplate - 配置
Remember-Me服务
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.rememberMe(rememberMe -> rememberMe
.key("uniqueAndSecretKey") // 仍然需要一个密钥用于签名Cookie,防止篡改
.tokenRepository(persistentTokenRepository()) // 注入持久化令牌仓库
.tokenValiditySeconds(3600 * 24 * 7) // 令牌有效期
.userDetailsService(userDetailsService) // 必须提供UserDetailsService
);
return http.build();
}
@Bean
public PersistentTokenRepository persistentTokenRepository() {
JdbcTokenRepositoryImpl tokenRepository = new JdbcTokenRepositoryImpl();
tokenRepository.setDataSource(dataSource);
// 如果表不存在,启动时自动创建(生产环境建议手动建表)
tokenRepository.setCreateTableOnStartup(true);
return tokenRepository;
}
5.优缺点
- 优点:
- 安全性高:令牌是随机生成的,不包含用户敏感信息。
- 支持撤销:可以主动使令牌失效。
- 防止重放攻击:通过令牌轮换机制,即使令牌被截获,攻击者也只能使用一次。
- 缺点:需要额外的数据库存储和维护。
四、核心组件解析
RememberMeAuthenticationFilter
- 继承自
OncePerRequestFilter,在doFilter方法中工作。 - 它从请求中读取
Remember-Me Cookie。 - 调用配置的
RememberMeServices(如PersistentTokenBasedRememberMeServices)的autoLogin方法进行验证。 - 验证成功后,创建
RememberMeAuthenticationToken并设置到SecurityContextHolder。
RememberMeServices接口
- 定义了
Remember-Me的核心服务方法:autoLogin(自动登录)、loginFail(登录失败处理)、loginSuccess(登录成功处理)、logout(登出处理)。 - 主要实现类:
TokenBasedRememberMeServices:简单哈希方式的实现。PersistentTokenBasedRememberMeServices:持久化令牌方式的实现。
RememberMeAuthenticationProvide
- 一个认证提供者,负责处理
RememberMeAuthenticationToken类型的认证。 - 一个认证提供者,负责处理
RememberMeAuthenticationToken类型的认证。
五、安全注意事项
- 密钥安全:
key(私有密钥)必须足够长、随机且严格保密。如果泄露,攻击者可以伪造任意用户的记住我令牌。 - 使用 HTTPS:务必配置
useSecureCookie(true),确保Cookie仅通过 HTTPS 传输,防止中间人攻击窃取Cookie。 - 设置 HttpOnly:配置
Cookie为HttpOnly,防止 XSS 攻击窃取Cookie。 - 合理的有效期:不要设置过长的
tokenValiditySeconds,在便利性和安全性之间取得平衡。 - 敏感操作需重新认证:对于修改密码、更改邮箱等敏感操作,即使有
Remember-Me Cookie,也应要求用户重新输入密码。 - 登出时清除:确保在用户主动登出时,调用
logout方法清除Remember-Me Cookie和数据库记录(对于持久化方式)。
总结
| 特性 | 简单哈希 (Simple Hash) | 持久化令牌 (Persistent Token) |
|---|---|---|
| 存储 | 无状态,无需存储 | 需要数据库存储令牌 |
| 安全性 | 较低,依赖密钥保密 | 较高,令牌随机且可轮换 |
| 撤销能力 | 无,无法主动撤销 | 有,可删除数据库记录 |
| 实现复杂度 | 简单 | 中等,需建表和配置 |
| 适用场景 | 简单应用,对安全要求不高 | 生产环境推荐,对安全要求高 |
在实际项目中,强烈建议使用持久化令牌(Persistent Token)方式,并配合 HTTPS、合理的密钥管理和令牌有效期,以在保证用户体验的同时,最大程度地保障应用安全。
更多推荐

所有评论(0)