Spring Security(三)(登录流程)

2018-06-04  本文已影响0人  夏目手札

前言

上一篇学习了security的Filter初始化顺序,现在来看一下登录验证的流程

正文

首先从上一部分了解到Filter中有一个
UsernamePasswordAuthenticationFilter
这个过滤器主要是进行用户验证,流程大致如下:


登录验证流程.png

UsernamePasswordAuthenticationFilter继承了AbstractAuthenticationProcessingFilter类,所以请求会先进入父类的doFilter方法

public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
            throws IOException, ServletException {

        HttpServletRequest request = (HttpServletRequest) req;
        HttpServletResponse response = (HttpServletResponse) res;
        if (!requiresAuthentication(request, response)) {
            chain.doFilter(request, response);

            return;
        }
        Authentication authResult;
        try {
            //调用子类的方法进行登录验证
            authResult = attemptAuthentication(request, response);
            if (authResult == null) {
                // return immediately as subclass has indicated that it hasn't completed
                // authentication
                return;
            }
            sessionStrategy.onAuthentication(authResult, request, response);
        }
        ....
        // Authentication success
        if (continueChainBeforeSuccessfulAuthentication) {
            chain.doFilter(request, response);
        }
        successfulAuthentication(request, response, chain, authResult);
    }

而attemptAuthentication方法中主要是调用调用AuthenticationManager的authenticate去验证,于是进入其子类ProviderManager中,

public Authentication authenticate(Authentication authentication)
            throws AuthenticationException {
        Class<? extends Authentication> toTest = authentication.getClass();
        AuthenticationException lastException = null;
        Authentication result = null;
        boolean debug = logger.isDebugEnabled();
        for (AuthenticationProvider provider : getProviders()) {
            if (!provider.supports(toTest)) {
                continue;
            }
            try {
                result = provider.authenticate(authentication);

                if (result != null) {
                    copyDetails(authentication, result);
                    break;
                }
            }
            .....
        }
        if (result == null && parent != null) {
            // Allow the parent to try.
            try {
                result = parent.authenticate(authentication);
            }
            catch (ProviderNotFoundException e) {
            }
            catch (AuthenticationException e) {
                lastException = e;
            }
        }
        .....
    }

其中循环调用AuthenticationProvider去进行权限验证,下面看一下抽象类AbstractUserDetailsAuthenticationProvider的authenticate方法

public Authentication authenticate(Authentication authentication)
            throws AuthenticationException {
        // Determine username
        String username = (authentication.getPrincipal() == null) ? "NONE_PROVIDED"
                : authentication.getName();
        boolean cacheWasUsed = true;
        UserDetails user = this.userCache.getUserFromCache(username);

        if (user == null) {
            cacheWasUsed = false;
            try {
                //获取用户相关信息
                user = retrieveUser(username,
                        (UsernamePasswordAuthenticationToken) authentication);
            }
            .....
        }

        try {
            preAuthenticationChecks.check(user);
            //进行验证
            additionalAuthenticationChecks(user,
                    (UsernamePasswordAuthenticationToken) authentication);
        }
        .....
        return createSuccessAuthentication(principalToReturn, authentication, user);
    }
protected final UserDetails retrieveUser(String username,
            UsernamePasswordAuthenticationToken authentication)
            throws AuthenticationException {
        prepareTimingAttackProtection();
        try {
            //根据配置的不同验证方式找到相应UserDetailService的loadUserByUsername方法获取用户
            UserDetails loadedUser = this.getUserDetailsService().loadUserByUsername(username);
            if (loadedUser == null) {
                throw new InternalAuthenticationServiceException(
                        "UserDetailsService returned null, which is an interface contract violation");
            }
            return loadedUser;
        }
        ......
    }

所以在第一部分中说了实现登录验证自定义配置时一定要实现UserDetailService的loadUserByUsername方法
事实上内存和JDBC验证方式也是实现了loadUserByUsername方法

总结

本次梳理下登录验证的流程,其实也挺简单的,下面开始梳理下权限验证流程

参考资料

官方文档

上一篇下一篇

猜你喜欢

热点阅读