第十四章 Remember-Me Authentication
Remember-Me 或者 persistent-login 验证是指网站能够在会话之间记住主体的身份。通常是向浏览器发送 cookie 来实现的,并且在后面的会话中,会去检测 cookie 并进行自动登录。
Spring Security 提供了两种方式来实现具体的 remember-me 操作,一种是使用 hash 来保护基于 cookie 令牌的安全性,另一种是使用数据库或其他持久存储机制来存储生成的令牌。
这两种方式都需要 UserDetailsService,否则身份验证程序将无法工作。
一、Simple Hash-Based Token Approach
使用散列来实现一个 remember-me 策略,在身份验证成功后,cookie会被发送到浏览器,其组成如下:
base64(username + ":" + expirationTime + ":" +
md5Hex(username + ":" + expirationTime + ":" + password + ":" + key))
在令牌的有效期内,用户名、密码、秘钥不能更改。这种方式存在一个安全问题,因为 remember-me 令牌可以从任何用户代理使用,直到令牌过期为止。那么如果一个主体知道了令牌已经被 captured,那么可以很容易地更改密码,并使所有的 remember-me 令牌失效。
Alternatively remember-me services should simply not be used at all. 因此,hash-based remember-me 不应该被使用。
二、Persistent Token Approach
持久令牌的方法,需要为 remember-me 配置一个持久化数据源,如下:
<http>
<remember-me data-srouce-ref="someDataSource"/>
</http>
该数据库需要包含一个 “persistent_logins” 表,表结构如下:
create table persistent_logins(
username varchar(64) not null,
series varchar(64) primary key,
token varchar(64) not null,
last_used timestamp not null
)
三、Remember-Me 的接口与实现
Remember-Me 通常与 UsernamePasswordAuthenticationFilter 一起使用,在通过用户名、密码方式登录时,选择是否记住用户密码。并且通过 AbstractAuthenticationProcessingFilter 父类的钩子来实现。
它也可以与 BasicAuthenticationFilter 一起使用,钩子会在适当的时候调用一个具体的 RememberMeServices 服务。
1. RememberMeServices Interface
以下是 RememberMeServices 接口的内容:
// 该接口为 remember-me 提供了基础的,与认证相关的事件通知
public interface RememberMeServices {
Authentication autoLogin(HttpServletRequest request, HttpServletResponse response);
void loginFail(HttpServletRequest request, HttpServletResponse response);
void loginSuccess(HttpServletRequest request, HttpServletResponse response, Authentication successfulAuthentication);
}
在 AbstractAuthenticationProcessingFilter 中只调用了 loginFail 和 loginSuccess,只要 SecurityContextHolder 中不包含身份验证,那么 RememberMeAuthenticationFilter 就会调用 autoLogin() 方法。
2. TokenBasedRememberMeServices Implements
TokenBasedRememberMeService 生成一个 RememberMeAuthenticationToken,并交给 RememberMeAuthenticationProvider 进行处理。
在 TokenBasedRememberMeService 的构造中,需要两个参数,一个是 key,另一个是 UserDetailsService,用于比较用户名和密码,并生成 RememberMeAuthenticationToken 来包含正确的 GrantedAuthority。
该 Service 还实现了 LogoutFilter,因此可以使用 LogoutFilter 自动清除 cookie。
3. PersistentTokenBasedRememberMeServices
与 TokenBasedRememberMeServices 一样,但是需要一个 PersistentTokenRepository 来存储这些令牌。
PersistentTokenRepository 有两个具体实现:
- InMemoryTokenRepositoryImpl
- JdbcTokenRepositoryImpl