• Springsecurity源码Filter之UsernamePasswordAuthenticationFilter(十四)


    最常用的一中过滤器,用于登录认证

    http.formLogin() 初始化

    类图

     AbstractAuthenticationProcessingFilter负责 认证成功和认证失败的调度 提供抽象方法attemptAuthentication 具体的认证逻辑由子类实现

    <1>

    org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter#doFilter

        private void doFilter(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
                throws IOException, ServletException {
            //判断是否是需要处理的请求  默认是/login
            if (!requiresAuthentication(request, response)) {
                chain.doFilter(request, response);
                return;
            }
            try {
                //<2>子类实现 认证 同时返回认证成功用户信息
                Authentication authenticationResult = attemptAuthentication(request, response);
                if (authenticationResult == null) {
                    // return immediately as subclass has indicated that it hasn't completed
                    return;
                }
                //登录成功的一些操作 比如sesion管理 超过最大登录限制 剔除其他session
                this.sessionStrategy.onAuthentication(authenticationResult, request, response);
                // 登录成功执行后续调用链
                if (this.continueChainBeforeSuccessfulAuthentication) {
                    chain.doFilter(request, response);
                }
                //登录成功处理逻辑 比如是否包含记住我的标识 如果有则设置cookie
                successfulAuthentication(request, response, chain, authenticationResult);
            }
            catch (InternalAuthenticationServiceException failed) {
                this.logger.error("An internal error occurred while trying to authenticate the user.", failed);
                unsuccessfulAuthentication(request, response, failed);
            }
            catch (AuthenticationException ex) {
                // 处理登录失败
                unsuccessfulAuthentication(request, response, ex);
            }
        }

    <2>

    org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter#attemptAuthentication

     @Override
        public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response)
                throws AuthenticationException {
            //是否只支持post请求
            if (this.postOnly && !request.getMethod().equals("POST")) {
                throw new AuthenticationServiceException("Authentication method not supported: " + request.getMethod());
            }
    
            //获取用户输入登录名
            String username = obtainUsername(request);
            username = (username != null) ? username : "";
            username = username.trim();
            //获取用户输入登录密码
            String password = obtainPassword(request);
            password = (password != null) ? password : "";
            //用名字和密码封装成UsernamePasswordAuthenticationToken
            UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(username, password);
            // 将request也封装到UsernamePasswordAuthenticationToken
            setDetails(request, authRequest);
            //交给AuthenticationManager处理,初始化处:<3> 本质是ProviceManger  authenticae则看<4>
            return this.getAuthenticationManager().authenticate(authRequest);
        }
        @Nullable
        protected String obtainUsername(HttpServletRequest request) {
            //从request 获取用户输入登录名 可以定制默认是username
            return request.getParameter(this.usernameParameter);
        }
    
        @Nullable
        protected String obtainPassword(HttpServletRequest request) {
            //从request 获取用户输入用户输入的密码 可以定制默认是password
            return request.getParameter(this.passwordParameter);
        }

    <3>

    可参考:https://www.cnblogs.com/LQBlog/p/15508248.html#autoid-9-0-0

    <4>

    我们可以自定义provider实现定制化登录逻辑如:

        @Override
        protected void configure(AuthenticationManagerBuilder auth) throws Exception {
            /**
             *  inMemoryAuthentication 开启在内存中定义用户
             *  多个用户通过and隔开
             */
            auth.authenticationProvider({}).inMemoryAuthentication()
                    .withUser("liqiang").password("liqiang").roles("admin")
                    .and()
                    .withUser("admin").password("admin").roles("admin");
        }

    org.springframework.security.authentication.ProviderManager#authenticate

    @Override
        public Authentication authenticate(Authentication authentication) throws AuthenticationException {
            Class<? extends Authentication> toTest = authentication.getClass();
            AuthenticationException lastException = null;
            AuthenticationException parentException = null;
            Authentication result = null;
            Authentication parentResult = null;
            int currentPosition = 0;
            int size = this.providers.size();
            //获取Providers 
            for (AuthenticationProvider provider : getProviders()) {
                //根据Authentication 判断是否能被处理
                if (!provider.supports(toTest)) {
                    continue;
                }
                if (logger.isTraceEnabled()) {
                    logger.trace(LogMessage.format("Authenticating request with %s (%d/%d)",
                            provider.getClass().getSimpleName(), ++currentPosition, size));
                }
                try {
                    //进行认真
                    result = provider.authenticate(authentication);
                    if (result != null) {
                        copyDetails(authentication, result);
                        break;
                    }
                }
                catch (AccountStatusException | InternalAuthenticationServiceException ex) {
                    //发布spring异常事件
                    prepareException(ex, authentication);
                
                    throw ex;
                }
                catch (AuthenticationException ex) {
                    lastException = ex;
                }
            }
            if (result == null && this.parent != null) {
                try {
                    //根据parent再校验一次
                    parentResult = this.parent.authenticate(authentication);
                    result = parentResult;
                }
                catch (ProviderNotFoundException ex) {
                    // ignore as we will throw below if no other exception occurred prior to
                    // calling parent and the parent
                    // may throw ProviderNotFound even though a provider in the child already
                    // handled the request
                }
                catch (AuthenticationException ex) {
                    parentException = ex;
                    lastException = ex;
                }
            }
            if (result != null) {
                if (this.eraseCredentialsAfterAuthentication && (result instanceof CredentialsContainer)) {
                    // Authentication is complete. Remove credentials and other secret data
                    // from authentication
                    ((CredentialsContainer) result).eraseCredentials();
                }
                if (parentResult == null) {
                    this.eventPublisher.publishAuthenticationSuccess(result);
                }
    
                return result;
            }
    
            if (lastException == null) {
                lastException = new ProviderNotFoundException(this.messages.getMessage("ProviderManager.providerNotFound",
                        new Object[] { toTest.getName() }, "No AuthenticationProvider found for {0}"));
            }
            if (parentException == null) {
                prepareException(lastException, authentication);
            }
            throw lastException;
        }
  • 相关阅读:
    考驾照的心得
    VS2005+ACCESS WEB程序出错数据访问权限错误的解决方法
    delphi开发回忆录——面向对象的基础,继承(续)
    用人不疑,疑人不用
    delphi开发回忆录——示例源码下载
    delphi开发回忆录——面向对象的基础,继承
    delphi开发回忆录——面向对象的基础,继承(续)
    delphi开发回忆录——示例源码下载
    考驾照的心得
    Win32 API消息函数:GetMessagePos
  • 原文地址:https://www.cnblogs.com/LQBlog/p/15534870.html
Copyright © 2020-2023  润新知