Spring-security源码-Filter之RememberMeAuthenticationFilter(十七)
实现记住密码的自动登录,比如session过期
初始化处
org.springframework.security.config.annotation.web.configurers.RememberMeConfigurer
RememberMeAuthenticationProvider的作用可以参考https://www.cnblogs.com/LQBlog/p/15534870.html#autoid-4-0-0
@SuppressWarnings("unchecked") @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) { //这里应该是退出登录 注入删除cookle的逻辑 logoutConfigurer.addLogoutHandler(this.logoutHandler); } RememberMeAuthenticationProvider authenticationProvider = new RememberMeAuthenticationProvider(key); authenticationProvider = postProcess(authenticationProvider); //主要看这里 在authenticationManager新增一个RememberMeAuthenticationProvider http.authenticationProvider(authenticationProvider); initDefaultLoginFilter(http); } @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); //新增一个remenberMeFilter http.addFilter(rememberMeFilter); }
RememberMeAuthenticationFilter
private void doFilter(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException, ServletException { //表示已经登录 直接放行 if (SecurityContextHolder.getContext().getAuthentication() != null) { this.logger.debug(LogMessage .of(() -> "SecurityContextHolder not populated with remember-me token, as it already contained: '" + SecurityContextHolder.getContext().getAuthentication() + "'")); chain.doFilter(request, response); return; } //获取Authentication 用户信息rememberMeServices 可以定制默认实现为PersistentTokenBasedRememberMeServices<2> Authentication rememberMeAuth = this.rememberMeServices.autoLogin(request, response); if (rememberMeAuth != null) { try { //这里有用户信息则调用authenticationManager 相应的Provider处理 参考:<4> rememberMeAuth = this.authenticationManager.authenticate(rememberMeAuth); // 设置到SecurityContextHolder.Context SecurityContextHolder.getContext().setAuthentication(rememberMeAuth); onSuccessfulAuthentication(request, response, rememberMeAuth); this.logger.debug(LogMessage.of(() -> "SecurityContextHolder populated with remember-me token: '" + SecurityContextHolder.getContext().getAuthentication() + "'")); if (this.eventPublisher != null) { this.eventPublisher.publishEvent(new InteractiveAuthenticationSuccessEvent( SecurityContextHolder.getContext().getAuthentication(), this.getClass())); } //自定义successHandler 处理逻辑 比如redirect 跳转到首页 if (this.successHandler != null) { this.successHandler.onAuthenticationSuccess(request, response, rememberMeAuth); return; } } catch (AuthenticationException ex) { this.logger.debug(LogMessage .format("SecurityContextHolder not populated with remember-me token, as AuthenticationManager " + "rejected Authentication returned by RememberMeServices: '%s'; " + "invalidating remember-me token", rememberMeAuth), ex); this.rememberMeServices.loginFail(request, response); //自动登录失败 onUnsuccessfulAuthentication(request, response, ex); } } chain.doFilter(request, response); }
<2>
org.springframework.security.web.authentication.rememberme.AbstractRememberMeServices#autoLogin
@Override public final Authentication autoLogin(HttpServletRequest request, HttpServletResponse response) { //获取cookile String rememberMeCookie = extractRememberMeCookie(request); if (rememberMeCookie == null) { return null; } this.logger.debug("Remember-me cookie detected"); if (rememberMeCookie.length() == 0) { this.logger.debug("Cookie was empty"); //清空Cookie的值 cancelCookie(request, response); return null; } try { String[] cookieTokens = decodeCookie(rememberMeCookie); //<3>子类实现根据Cookie获取UserDetails UserDetails user = processAutoLoginCookie(cookieTokens, request, response); //检查逻辑,比如检查记住登录 只能存在指定时间 this.userDetailsChecker.check(user); this.logger.debug("Remember-me cookie accepted"); //子类实现创建Authentication return createSuccessfulAuthentication(request, user); } catch (CookieTheftException ex) { //清空Cookie的值 cancelCookie(request, response); throw ex; } catch (UsernameNotFoundException ex) { this.logger.debug("Remember-me login was valid but corresponding user not found.", ex); } catch (InvalidCookieException ex) { this.logger.debug("Invalid remember-me cookie: " + ex.getMessage()); } catch (AccountStatusException ex) { this.logger.debug("Invalid UserDetails: " + ex.getMessage()); } catch (RememberMeAuthenticationException ex) { this.logger.debug(ex.getMessage()); } cancelCookie(request, response); return null; }
<3>
org.springframework.security.web.authentication.rememberme.PersistentTokenBasedRememberMeServices#processAutoLoginCookie
protected UserDetails processAutoLoginCookie(String[] cookieTokens, HttpServletRequest request, HttpServletResponse response) { if (cookieTokens.length != 2) { throw new InvalidCookieException("Cookie token did not contain " + 2 + " tokens, but contained '" + Arrays.asList(cookieTokens) + "'"); } String presentedSeries = cookieTokens[0]; String presentedToken = cookieTokens[1]; //通过tokenRepository 获取 PersistentRememberMeToken PersistentRememberMeToken token = this.tokenRepository.getTokenForSeries(presentedSeries); if (token == null) { // No series match, so we can't authenticate using this cookie throw new RememberMeAuthenticationException("No persistent token found for series id: " + presentedSeries); } // token是否相等 if (!presentedToken.equals(token.getTokenValue())) { //不相等移除 this.tokenRepository.removeUserTokens(token.getUsername()); throw new CookieTheftException(this.messages.getMessage( "PersistentTokenBasedRememberMeServices.cookieStolen", "Invalid remember-me token (Series/token) mismatch. Implies previous cookie theft attack.")); } if (token.getDate().getTime() + getTokenValiditySeconds() * 1000L < System.currentTimeMillis()) { throw new RememberMeAuthenticationException("Remember-me login has expired"); } //通过PersistentRememberMeToken 封装 PersistentRememberMeToken newToken = new PersistentRememberMeToken(token.getUsername(), token.getSeries(), generateTokenData(), new Date()); try { //修改时间 this.tokenRepository.updateToken(newToken.getSeries(), newToken.getTokenValue(), newToken.getDate()); addCookie(newToken, request, response); } catch (Exception ex) { this.logger.error("Failed to update token: ", ex); throw new RememberMeAuthenticationException("Autologin failed due to data access problem"); } //根据用户名获得userDetail return getUserDetailsService().loadUserByUsername(token.getUsername()); }