上一篇《Spring Security 是如何“记住我”的? 》介绍了,Spring Security 的 RememberMe 功能的实现原理,我们可以在 Spring Security 的配置类中,对 HttpSecurity 对象调用一个 rememberMe()
方法,就可以开启这个功能。这一篇,我们来试着分析一下,在这个方法中,Spring Security 做了哪些事情。
如果你没读过前面那篇,可以先去读一下。
先从 rememberMe()
方法入手
查看 rememberMe()
方法的源码:
public RememberMeConfigurer<HttpSecurity> rememberMe() throws Exception {
return getOrApply(new RememberMeConfigurer<>());
}
很简洁,就一句,配置了一个 RememberMeConfigurer
,然后,我们重点看这个类。
RememberMeConfigurer
这个配置类里面有两个方法值得注意,一个是 init
方法,另一个是 configure
方法。我们一一来看。
init
方法
先看 init
方法的源码。
@Override
public void init(H http) throws Exception {
validateInput();
String key = getKey();
RememberMeServices rememberMeServices = getRememberMeServices(http, key);
http.setSharedObject(RememberMeServices.class, rememberMeServices);
LogoutConfigurer<H> logoutConfigurer = http.getConfigurer(LogoutConfigurer.class);
if (logoutConfigurer != null && this.logoutHandler != null) {
logoutConfigurer.addLogoutHandler(this.logoutHandler);
}
RememberMeAuthenticationProvider authenticationProvider = new RememberMeAuthenticationProvider(key);
authenticationProvider = postProcess(authenticationProvider);
http.authenticationProvider(authenticationProvider);
initDefaultLoginFilter(http);
}
这个方法里一共干了 3 件事儿,我们一个一个看。
配置 RememberMeServices
RememberMeServices
是通过这样代码创建的:
RememberMeServices rememberMeServices = getRememberMeServices(http, key);
我们找到 getRememberMeServices
的源码查看:
private RememberMeServices getRememberMeServices(H http, String key) throws Exception {
if (this.rememberMeServices != null) {
if (this.rememberMeServices instanceof LogoutHandler && this.logoutHandler == null) {
this.logoutHandler = (LogoutHandler) this.rememberMeServices;
}
return this.rememberMeServices;
}
AbstractRememberMeServices tokenRememberMeServices = createRememberMeServices(http, key);
tokenRememberMeServices.setParameter(this.rememberMeParameter);
tokenRememberMeServices.setCookieName(this.rememberMeCookieName);
if (this.rememberMeCookieDomain != null) {
tokenRememberMeServices.setCookieDomain(this.rememberMeCookieDomain);
}
if (this.tokenValiditySeconds != null) {
tokenRememberMeServices.setTokenValiditySeconds(this.tokenValiditySeconds);
}
if (this.useSecureCookie != null) {
tokenRememberMeServices.setUseSecureCookie(this.useSecureCookie);
}
if (this.alwaysRemember != null) {
tokenRememberMeServices.setAlwaysRemember(this.alwaysRemember);
}
tokenRememberMeServices.afterPropertiesSet();
this.logoutHandler = tokenRememberMeServices;
this.rememberMeServices = tokenRememberMeServices;
return tokenRememberMeServices;
}
我们从上到下解析:
- 如果已经配置了
rememberMeServices
,则返回,否则继续向下执行。在返回之前,还会判断它是否是LogoutHandler
的实现类,如果是的话,将其父只给logoutHandler
属性。 - 通过
createRememberMeServices
方法,创建一个AbstractRememberMeServices
对象,并且配置一些属性,并将其赋值给当前配置类的logoutHandler
和rememberMeServices
属性,最后返回。
这里有一点有必要交代,之所以要将创建的 tokenRememberMeServices
赋值给 logoutHandler
,是因为 AbstractRememberMeServices
类也实现了 LogoutHandler
接口,这个接口只有一个 logout
方法,实现这个方法后,就可以在用户注销登录之后,删除 RememberMeToken 及相关的信息。
这里我们还需要关注一下 createRememberMeServices
方法,是如何创建 AbstractRememberMeServices
对象的。这个方法的源码如下:
private AbstractRememberMeServices createRememberMeServices(H http, String key) {
return (this.tokenRepository != null) ? createPersistentRememberMeServices(http, key)
: createTokenBasedRememberMeServices(http, key);
}
上一篇文章(链接在本文开头)中,我曾经说过:
默认情况下 Spring Security 会使用
TokenBasedRememberMeServices
,提供了基础的功能。如果我们在开启 RememberMe 功能的时候,同时配置了一个PersistentTokenRepository
,那么 Spring Security 会自动选择PersistentTokenBasedRememberMeServices
的实现。
这个判断逻辑就是在这里完成的。
至此,init
方法中,配置 RememberMeServices
的逻辑,我们就分析到这里。
配置 logoutHandler
接下来,会配置 logoutHandler
,我们刚才通过源码分析过,在配置 RememberMeServices
的过程中,已经又了 logoutHandler
属性的值,这里只需要将其配置好,它的作用就是在用户注销登录后,删除 RememberMeToken 及相关的信息。
这一逻辑也可以从 AbstractRememberMeServices 的 logout 方法的源码中看出来:
@Override
public void logout(HttpServletRequest request, HttpServletResponse response, Authentication authentication) {
this.logger.debug(LogMessage
.of(() -> "Logout of user " + ((authentication != null) ? authentication.getName() : "Unknown")));
cancelCookie(request, response);
}
配置 RememberMeAuthenticationProvider
上一篇文章(链接在本文开头)中我们也说到过,在RememberMeAuthenticationFilter
中,需要调用 authenticationManager.authenticate
方法进行身份认证。如果你了解 Spring Security 的认证流程的话就会知道,每一种认证方式都需要有一个对应的 Provider 来完成具体的认证工作。RememberMeAuthenticationProvider
就是来完成 RememberMe 认证的那个 Provider。
想要了解 Spring Security 的认证流程源码分析,可以参考我的这篇文章:Spring Security 认证流程 。
以上,就是 init
方法完成的工作。接下来看 configure
方法。
configure
方法
看源码:
@Override
public void configure(H http) {
RememberMeAuthenticationFilter rememberMeFilter = new RememberMeAuthenticationFilter(
http.getSharedObject(AuthenticationManager.class), this.rememberMeServices);
if (this.authenticationSuccessHandler != null) {
rememberMeFilter.setAuthenticationSuccessHandler(this.authenticationSuccessHandler);
}
rememberMeFilter = postProcess(rememberMeFilter);
http.addFilter(rememberMeFilter);
}
这里比较简单,就是通过 AuthenticationManager 和刚才创建的 rememberMeServices 创建了一个 RememberMeAuthenticationFilter 过滤器,并将其加入过滤器链。RememberMe 相关的认证逻辑,正是在这个过滤器中调用的。