在我们登录一些网站的时候,通常会有一个选项-记住我。选中该选项,登录成功后,无论浏览器关闭,还是服务重启,只要不清除浏览器缓存信息,且Cookie在网站预设的有效期时间内,则无需重新登录网站即可访问其资源。
其实,Spring Security 框架也可以实现相同的功能。本文便基于 Spring Security 框架默认的用户名密码登录为基础,为其添加 RememberMe(记住我)选项。
首先,修改登录页面,将 记住我 复选框 name 属性修改为 remember-me。
"@{/login}" method= ......
class="checkbox"> <label><input type="checkbox" name="remember-me"> 记住我label> div> ......</form>为何要修改 name 属性修改为 remember-me,而不是别的什么名字呢?其实,原因并不复杂,简单点来说,这是 Spring Security 框架默认的 记住我 复选框的名称。当然,你也可以命名为别的名字,然后把相关配置也同样修改为此名字即可。
还记得 UsernamePasswordAuthenticationFilter 类有一个 rememberMeServices 属性吗?初始值为 NullRememberMeServices。
private RememberMeServices rememberMeServices = new NullRememberMeServices();
private RememberMeServices rememberMeServices = new NullRememberMeServices();
在身份认证成功后,会执行 successfulAuthentication 方法。这在文章史上最简单的Spring Security教程(二十五):UsernamePasswordAuthenticationFilter详解中也有提及,不过并没有过多的解释。此方法执行过程中,便会调用 RememberMeServices 接口的 loginSuccess 方法。
protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain, Authentication authResult) throws IOException, ServletException { if (logger.isDebugEnabled()) { logger.debug("Authentication success. Updating SecurityContextHolder to contain: " + authResult); } SecurityContextHolder.getContext().setAuthentication(authResult); rememberMeServices.loginSuccess(request, response, authResult); // Fire event if (this.eventPublisher != null) { eventPublisher.publishEvent(new InteractiveAuthenticationSuccessEvent( authResult, this.getClass())); } successHandler.onAuthenticationSuccess(request, response, authResult);}
protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain, Authentication authResult) throws IOException, ServletException { if (logger.isDebugEnabled()) { logger.debug("Authentication success. Updating SecurityContextHolder to contain: " + authResult); } SecurityContextHolder.getContext().setAuthentication(authResult); rememberMeServices.loginSuccess(request, response, authResult); // Fire event if (this.eventPublisher != null) { eventPublisher.publishEvent(new InteractiveAuthenticationSuccessEvent( authResult, this.getClass())); } successHandler.onAuthenticationSuccess(request, response, authResult);}
RememberMeServices 接口的 loginSuccess 便会判断登录页的 记住我 复选框是否选中,选中的情况下,才会执行后续逻辑。
public final void loginSuccess(HttpServletRequest request, HttpServletResponse response, Authentication successfulAuthentication) { if (!rememberMeRequested(request, parameter)) { logger.debug("Remember-me login not requested."); return; } onLoginSuccess(request, response, successfulAuthentication);}
public final void loginSuccess(HttpServletRequest request, HttpServletResponse response, Authentication successfulAuthentication) { if (!rememberMeRequested(request, parameter)) { logger.debug("Remember-me login not requested."); return; } onLoginSuccess(request, response, successfulAuthentication);}
注意,这是 RememberMeServices 接口的 抽象实现类 AbstractRememberMeServices 中的实现逻辑,由于 UsernamePasswordAuthenticationFilter 类 rememberMeServices 属性的初始值为 NullRememberMeServices,其并没有继承 AbstractRememberMeServices 类,只是空的实现。因此,需要配置 UsernamePasswordAuthenticationFilter 类中的 rememberMeServices 属性为具体的 RememberMeServices 接口实现。
然后,调整一下 Spring Security 配置,新增 rememberMe 的相关配置。
protected void configure(HttpSecurity http) throws Exception { http ...... .rememberMe() .userDetailsService(userDetailsService()) .tokenValiditySeconds(14 * 24 * 60 * 60) ......}
protected void configure(HttpSecurity http) throws Exception { http ...... .rememberMe() .userDetailsService(userDetailsService()) .tokenValiditySeconds(14 * 24 * 60 * 60) ......}
token默认有效期为两周,即14天,与 Spring Security 默认配置相同。万事俱备,启动系统,访问登录页面,尝试一下。
输入用户名、密码,最重要的不要忘了,勾选 记住我 选项,系统正常调转到了系统首页。关闭浏览器,直接访问系统首页,此时,仍然可以直接访问,并没有重定向到登录页,无需输入用户名、密码。相同的,重启应用之后,同样可以直接访问系统首页,也无需输入用户名、密码。记住我 改造完成。其它详细源码,请参考文末源码链接,可自行下载后阅读。当然,如果文章有错误,或者您有任何的意见或建议,请留言。感谢您的阅读!源码githubhttps://github.com/liuminglei/SpringSecurityLearning/tree/master/31gitee
https://gitee.com/xbd521/SpringSecurityLearning/tree/master/31