zoukankan      html  css  js  c++  java
  • Springsecurity源码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());
        }

      

  • 相关阅读:
    .net Dapper 学习系列(1) ---Dapper入门
    ASP.NET WebApi 学习与实践系列(2)---WebApi 路由请求的理解
    ASP.NET WebApi 学习与实践系列(1)---如何创建 WebApi
    Sqlserver 总结(2) 存储过程
    Image.Save出错 GDI 一般错误
    python导入模块的两种方式
    从1KW条数据中筛选出1W条最大的数
    C# EF更新当前实体报错 ObjectManager无法管理具有相同键值的多个对象
    微信小程序 异步请求拿数据+使用外部js库
    微信小程序入门 第一个页面搭建
  • 原文地址:https://www.cnblogs.com/LQBlog/p/15539504.html
Copyright © 2011-2022 走看看