时间:2021-05-20
这章继续扩展功能,来一个“记住我”的功能实现,就是说用户在登录一次以后,系统会记住这个用户一段时间,这段时间内用户不需要重新登录就可以使用系统。
记住我功能基本原理
原理说明
图解说明
RememberMeAuthenticationFilter位于过滤器链的哪一环?
图解
首先其他认证过滤器会先进行认证,当其他过滤器都无法认证时,RememberMeAuthenticationFilter会尝试去做认证。
记住我功能具体实现
前端页面
登录的时候加上一行记住我的勾选按钮,这里要注意,name一定要是remember-me,下面源码部分会提到。
<tr> <td colspan='2'><input name="remember-me" type="checkbox" value="true" />记住我</td> </tr>后台
首先配置TokenRepositoryBean
/** * 记住我功能的Token存取器配置 * * @return */ @Bean public PersistentTokenRepository persistentTokenRepository() { JdbcTokenRepositoryImpl tokenRepository = new JdbcTokenRepositoryImpl(); tokenRepository.setDataSource(dataSource); // 启动的时候自动创建表,建表语句 JdbcTokenRepositoryImpl 已经都写好了 tokenRepository.setCreateTableOnStartup(true); return tokenRepository; }然后需要在 configure 配置方法那边进行记住我功能所有组件的配置
protected void configure(HttpSecurity http) throws Exception { ValidateCodeFilter validateCodeFilter = new ValidateCodeFilter(); http.addFilterBefore(validateCodeFilter, UsernamePasswordAuthenticationFilter.class) .formLogin() .loginPage("/authentication/require") .loginProcessingUrl("/authentication/form") .successHandler(meicloudAuthenticationSuccessHandler) .failureHandler(meicloudAuthenticationFailureHandler) // 配置记住我功能 .and() .rememberMe() // 配置TokenRepository .tokenRepository(persistentTokenRepository()) // 配置Token过期时间 .tokenValiditySeconds(3600) // 最终拿到用户名之后,使用UserDetailsService去做登录 .userDetailsService(userDetailsService) .and() .authorizeRequests() .antMatchers("/authentication/require", securityProperties.getBrowser().getSignInPage(), "/code/image").permitAll() .anyRequest() .authenticated() .and() .csrf().disable(); }记住我功能Spring Security源码解析
登录之前“记住我”源码流程
在认证成功之后,会调用successfulAuthentication方法(这些第五章源码部分已经学习过),在将认证信息保存到Context后,RememberMeServices就会调用它的loginSuccess方法。
protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain, Authentication authResult) throws IOException, ServletException { if (this.logger.isDebugEnabled()) { this.logger.debug("Authentication success. Updating SecurityContextHolder to contain: " + authResult); } SecurityContextHolder.getContext().setAuthentication(authResult); this.rememberMeServices.loginSuccess(request, response, authResult); if (this.eventPublisher != null) { this.eventPublisher.publishEvent(new InteractiveAuthenticationSuccessEvent(authResult, this.getClass())); } this.successHandler.onAuthenticationSuccess(request, response, authResult); }loginSuccess方法里面会先检查请求中是否有name为remember-me的参数,有才进行下一步。
public final void loginSuccess(HttpServletRequest request, HttpServletResponse response, Authentication successfulAuthentication) { // this.parameter = "remember-me" if (!this.rememberMeRequested(request, this.parameter)) { this.logger.debug("Remember-me login not requested."); } else { this.onLoginSuccess(request, response, successfulAuthentication); } }再进入onLoginSuccess方法,里面主要就是进行写库和写Cookie的操作。
protected void onLoginSuccess(HttpServletRequest request, HttpServletResponse response, Authentication successfulAuthentication) { String username = successfulAuthentication.getName(); this.logger.debug("Creating new persistent login for user " + username); // 生成Token PersistentRememberMeToken persistentToken = new PersistentRememberMeToken(username, this.generateSeriesData(), this.generateTokenData(), new Date()); try { // 将Token和userName插入数据库 this.tokenRepository.createNewToken(persistentToken); // 将Token写到Cookie中 this.addCookie(persistentToken, request, response); } catch (Exception var7) { this.logger.error("Failed to save persistent token ", var7); } }登录之后“记住我”源码流程
首先会进入RememberMeAuthenticationFilter,会先判断前面的过滤器是否进行过认证(Context中是否有认证信息),未进行过认证的话会调用RememberMeServices的autoLogin方法。
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException { HttpServletRequest request = (HttpServletRequest)req; HttpServletResponse response = (HttpServletResponse)res; if (SecurityContextHolder.getContext().getAuthentication() == null) { Authentication rememberMeAuth = this.rememberMeServices.autoLogin(request, response); if (rememberMeAuth != null) { try { rememberMeAuth = this.authenticationManager.authenticate(rememberMeAuth); SecurityContextHolder.getContext().setAuthentication(rememberMeAuth); this.onSuccessfulAuthentication(request, response, rememberMeAuth); if (this.logger.isDebugEnabled()) { this.logger.debug("SecurityContextHolder populated with remember-me token: '" + SecurityContextHolder.getContext().getAuthentication() + "'"); } if (this.eventPublisher != null) { this.eventPublisher.publishEvent(new InteractiveAuthenticationSuccessEvent(SecurityContextHolder.getContext().getAuthentication(), this.getClass())); } if (this.successHandler != null) { this.successHandler.onAuthenticationSuccess(request, response, rememberMeAuth); return; } } catch (AuthenticationException var8) { if (this.logger.isDebugEnabled()) { this.logger.debug("SecurityContextHolder not populated with remember-me token, as AuthenticationManager rejected Authentication returned by RememberMeServices: '" + rememberMeAuth + "'; invalidating remember-me token", var8); } this.rememberMeServices.loginFail(request, response); this.onUnsuccessfulAuthentication(request, response, var8); } } chain.doFilter(request, response); } else { if (this.logger.isDebugEnabled()) { this.logger.debug("SecurityContextHolder not populated with remember-me token, as it already contained: '" + SecurityContextHolder.getContext().getAuthentication() + "'"); } chain.doFilter(request, response); } }autoLogin方法里面,主要调用this.processAutoLoginCookie(cookieTokens, request, response)这个方法获取数据库中的用户信息,其步骤是:
回到RememberMeAuthenticationFilter,在调用了autoLogin方法之后得到了rememberMeAuth,然后再对其进行一个认证,认证成功之后保存到SecurityContext中,至此整个RememberMe自动登录流程源码结束。
相关阅读:
Spring Security实现图形验证码登录
Spring Security实现短信验证码登录
总结
到此这篇关于Spring Security 实现“记住我”功能及原理解析的文章就介绍到这了,更多相关spring security记住我内容请搜索以前的文章或继续浏览下面的相关文章希望大家以后多多支持!
声明:本页内容来源网络,仅供用户参考;我单位不保证亦不表示资料全面及准确无误,也不保证亦不表示这些资料为最新信息,如因任何原因,本网内容或者用户因倚赖本网内容造成任何损失或损害,我单位将不会负任何法律责任。如涉及版权问题,请提交至online#300.cn邮箱联系删除。
spring-security里自带了oauth2,正好YIIU里也用到了spring-security做权限部分,那为何不直接集成上第三方登录呢?然后我开始了
这里使用的是spring-security和原生的jasigcas包来进行整合,为什么没有直接使用spring提供的spring-security-cas,后面
这篇文章主要介绍了Spring注解@RestControllerAdvice原理解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值
这篇文章主要介绍了Spring@Conditional注解原理解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参
接触Spring快半年了,前段时间刚用Spring4+S2H4做完了自己的毕设,但是很明显感觉对Spring尤其是IOC容器的实现原理理解的不到位,说白了,就是