用户登录:Spring Security3源码分析-UsernamePasswordAuthenticationFilter分析

原文链接:http://dead-knight.iteye.com/blog/1513149 好好

参考链接2:http://blog.csdn.net/yecong111/article/details/17015199

如果用户没有自定义登录的url使用默认的url,路径是url:j_spring_security_check

过滤器UsernamePasswordAuthenticationFilter过滤到这个url。(实际上这个Filter类的doFilter是父类AbstractAuthenticationProcessingFilter的)。

  AbstractAuthenticationProcessingFilter调用UsernamePasswordAuthenticationFilter中的attemptAuthentication方法,将表单请求的信息(用户、密码等信息)赋值给UsernamePasswordAuthenticationToken(authRequest),然后调用AbstractAuthenticationProcessingFilter中的AuthenticationManager类中的(默认是ProviderManager类)方法——getAuthenticationManager().authenticate(authRequest)对用户密码的正确性进行验证,认证失败就抛出异常,成功就返回Authentication对象。

UsernamePasswordAuthenticationFilter代码

    public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {
        //只处理post提交的请求
        if (postOnly && !request.getMethod().equals("POST")) {
            throw new AuthenticationServiceException("Authentication method not supported: " + request.getMethod());
        }
        //获取用户名、密码数据
        String username = obtainUsername(request);
        String password = obtainPassword(request);

        if (username == null) {
            username = "";
        }

        if (password == null) {
            password = "";
        }

        username = username.trim();
        //构造未认证的UsernamePasswordAuthenticationToken
        UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(username, password);

        // Place the last username attempted into HttpSession for views
        HttpSession session = request.getSession(false);
        //如果session不为空,添加username到session中
        if (session != null || getAllowSessionCreation()) {
            request.getSession().setAttribute(SPRING_SECURITY_LAST_USERNAME_KEY, TextEscapeUtils.escapeEntities(username));
        }

        // Allow subclasses to set the "details" property
        //设置details,这里就是设置org.springframework.security.web.
        //authentication.WebAuthenticationDetails实例到details中
        setDetails(request, authRequest);
        //通过AuthenticationManager:ProviderManager完成认证任务
        return this.getAuthenticationManager().authenticate(authRequest);
    }

this.getAuthenticationManager().authenticate(authRequest)是指ProviderManager类中authenticate方法,ProviderManager类中authenticate方法里校验用户名密码的核心是providers。

ProviderManager代码

    public Authentication doAuthentication(Authentication authentication) throws AuthenticationException {
        Class<? extends Authentication> toTest = authentication.getClass();
        AuthenticationException lastException = null;
        Authentication result = null;
        //循环ProviderManager中的providers,由具体的provider执行认证操作
        for (AuthenticationProvider provider : getProviders()) {
        	System.out.println("AuthenticationProvider: " + provider.getClass().getName());
            if (!provider.supports(toTest)) {
                continue;
            }

            logger.debug("Authentication attempt using " + provider.getClass().getName());

            try {
                result = provider.authenticate(authentication);

                if (result != null) {
                    //复制details
                    copyDetails(authentication, result);
                    break;
                }
            } catch (AccountStatusException e) {
                // SEC-546: Avoid polling additional providers if auth failure is due to invalid account status
                eventPublisher.publishAuthenticationFailure(e, authentication);
                throw e;
            } catch (AuthenticationException e) {
                lastException = e;
            }
        }

        if (result == null && parent != null) {
            // Allow the parent to try.
            try {
                result = parent.authenticate(authentication);
            } catch (ProviderNotFoundException e) {
                // 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 e) {
                lastException = e;
            }
        }

        if (result != null) {
            eventPublisher.publishAuthenticationSuccess(result);
            return result;
        }

        // Parent was null, or didn't authenticate (or throw an exception).

        if (lastException == null) {
            lastException = new ProviderNotFoundException(messages.getMessage("ProviderManager.providerNotFound",
                        new Object[] {toTest.getName()}, "No AuthenticationProvider found for {0}"));
        }
        //由注入进来的org.springframework.security.authentication.DefaultAuthenticationEventPublisher完成事件发布任务
        eventPublisher.publishAuthenticationFailure(lastException, authentication);

        throw lastException;
    }

ProviderManager仅仅是管理provider的,具体的authenticate认证任务由各自provider来完成。 providers中的provider分别为: 
org.springframework.security.authentication.dao.DaoAuthenticationProvider 
org.springframework.security.authentication.AnonymousAuthenticationProvider 
其他的provider根据特殊情况,再添加到providers中的,如remember me功能的provider 
org.springframework.security.authentication.RememberMeAuthenticationProvider 。

AuthenticationProvider接口里有authenticate方法,实现类有多个:其中有AbstractUserDetailsAuthenticationProvider,AbstractUserDetailsAuthenticationProvider的子类DaoAuthenticationProvider(上文提到的providers中的provider) 中有retrieveUser方法,retrieveUser方法中有最重要的一句代码:

loadedUser = this.getUserDetailsService().loadUserByUsername(username);

这行代码中调用了UserDetailsService,我们通过实现UserDetailsService重写loadUserByUsername方法,就可以用查询数据库的方式从数据库表中获取用户名密码,而不是在配置文件中写死用户名密码。


    原文作者:Spring Boot
    原文地址: https://blog.csdn.net/wabiaozia/article/details/62041365
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞