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());
    }

  

posted @ 2021-11-11 13:41  意犹未尽  阅读(153)  评论(0编辑  收藏  举报