Spring-security源码-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;
    }
posted @ 2021-11-10 19:05  意犹未尽  阅读(260)  评论(0编辑  收藏  举报