美文网首页
Spring Security(三)(登录流程)

Spring Security(三)(登录流程)

作者: 夏目手札 | 来源:发表于2018-06-04 23:36 被阅读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方法

    总结

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

    参考资料

    官方文档

    相关文章

      网友评论

          本文标题:Spring Security(三)(登录流程)

          本文链接:https://www.haomeiwen.com/subject/szmtsftx.html