串理spring security认证流程源码

1.认证流程流程

通过断点调试,可以看到在UsernamepasswordAuthenticationFilter中构造了一个
UsernamePasswordAuthenticationToken对象


打开UsernamePasswordAuthenticationToken可得知,该实现类是Authentication的子类,因为Authentication是封装了用户的信息。
在该构造函数中,其中super(null)是调用了父类的方法

父类的方法如下:

    public AbstractAuthenticationToken(Collection<? extends GrantedAuthority> authorities) {
        if (authorities == null) { //为空时,需要赋个默认的权限,因为此时还未进行身份认证
            this.authorities = AuthorityUtils.NO_AUTHORITIES;
            return;
        }

        for (GrantedAuthority a : authorities) {
            if (a == null) {
                throw new IllegalArgumentException(
                        "Authorities collection cannot contain any null elements");
            }
        }
        ArrayList<GrantedAuthority> temp = new ArrayList<GrantedAuthority>(
                authorities.size());
        temp.addAll(authorities);
        this.authorities = Collections.unmodifiableList(temp);
    }

setAuthenticated(false) 代表当前存进去的principal/credentials是否经过身份认证,此时肯定是没有的。

setDetails(request, authRequest);

该方法会把请求的一些信息设置到UsernamePasswordAuthenticationToken里面去,包括当前发起请求的IP,Session等

return this.getAuthenticationManager().authenticate(authRequest); //往AuthenticationManager靠拢

AuthenticationManager该类本身不包含校验的逻辑,它的作用是用来管理AuthenticationProvider
该方法会请求进入:ProviderManager.authenticate()方法,该类实现了AuthenticationManager接口

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;
            }
            /*
            getProviders() 拿到所有的AuthenticationProvider,校验逻辑都是在Provider中
            因为不同的登录方式,它的认证逻辑是不通 的,目前使用的用户名+密码的方式,后续还会有第三方登录,手机    号验证码登录等,provider.supports(toTest)是否支持当前的登录方式(判断)
            对于用户名密码方式,它传递的token是:UsernamePasswordAuthenticationToken,而对于第三方登录时的验证方式则是SocialAuthenticationToken
            */

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

            try {
            //具体执行校验逻辑
                result = provider.authenticate(authentication);

                if (result != null) {
                    copyDetails(authentication, result);
                    break;
                }
            }
            catch (AccountStatusException e) {
                prepareException(e, authentication);
                throw e;
            }
            catch (InternalAuthenticationServiceException e) {
                prepareException(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) {
            }
            catch (AuthenticationException e) {
                lastException = e;
            }
        }

        if (result != null) {
            if (eraseCredentialsAfterAuthentication
                    && (result instanceof CredentialsContainer)) {
                ((CredentialsContainer) result).eraseCredentials();
            }

            eventPublisher.publishAuthenticationSuccess(result);
            return result;
        }

        if (lastException == null) {
            lastException = new ProviderNotFoundException(messages.getMessage(
                    "ProviderManager.providerNotFound",
                    new Object[] { toTest.getName() },
                    "No AuthenticationProvider found for {0}"));
        }

        prepareException(lastException, authentication);

        throw lastException;
    }

provider.authenticate()实现类是写在AuthenticationProvider的实现类AbstractUserDetailsAuthenticationProvider中

public Authentication authenticate(Authentication authentication)
            throws AuthenticationException {
        Assert.isInstanceOf(UsernamePasswordAuthenticationToken.class, authentication,
                messages.getMessage(
                        "AbstractUserDetailsAuthenticationProvider.onlySupports",
                        "Only UsernamePasswordAuthenticationToken is supported"));

        String username = (authentication.getPrincipal() == null) ? "NONE_PROVIDED"
                : authentication.getName();

        boolean cacheWasUsed = true;
        UserDetails user = this.userCache.getUserFromCache(username);

        if (user == null) {
            cacheWasUsed = false;

            try {
            //获取用户信息,具体实现类在:DaoAuthenticationProvider.retrieveUser()
                user = retrieveUser(username,
                        (UsernamePasswordAuthenticationToken) authentication);
            }
            catch (UsernameNotFoundException notFound) {
                logger.debug("User ‘" + username + "‘ not found");

                if (hideUserNotFoundExceptions) {
                    throw new BadCredentialsException(messages.getMessage(
                            "AbstractUserDetailsAuthenticationProvider.badCredentials",
                            "Bad credentials"));
                }
                else {
                    throw notFound;
                }
            }

            Assert.notNull(user,
                    "retrieveUser returned null - a violation of the interface contract");
        }

        try {
            preAuthenticationChecks.check(user);
            additionalAuthenticationChecks(user,
                    (UsernamePasswordAuthenticationToken) authentication);
        }
        catch (AuthenticationException exception) {
            if (cacheWasUsed) {
                cacheWasUsed = false;
                user = retrieveUser(username,
                        (UsernamePasswordAuthenticationToken) authentication);
                preAuthenticationChecks.check(user);
                additionalAuthenticationChecks(user,
                        (UsernamePasswordAuthenticationToken) authentication);
            }
            else {
                throw exception;
            }
        }

        postAuthenticationChecks.check(user);

        if (!cacheWasUsed) {
            this.userCache.putUserInCache(user);
        }

        Object principalToReturn = user;

        if (forcePrincipalAsString) {
            principalToReturn = user.getUsername();
        }

        return createSuccessAuthentication(principalToReturn, authentication, user);
    }
    protected final UserDetails retrieveUser(String username,
            UsernamePasswordAuthenticationToken authentication)
            throws AuthenticationException {
        UserDetails loadedUser;

        try {
            loadedUser = this.getUserDetailsService().loadUserByUsername(username);
            /**
            getUserDetailService在调用我们提供的UserDetailService的实现,也就是:MyUserDetailsService

            */
        }
        catch (UsernameNotFoundException notFound) {
            if (authentication.getCredentials() != null) {
                String presentedPassword = authentication.getCredentials().toString();
                passwordEncoder.isPasswordValid(userNotFoundEncodedPassword,
                        presentedPassword, null);
            }
            throw notFound;
        }
        catch (Exception repositoryProblem) {
            throw new InternalAuthenticationServiceException(
                    repositoryProblem.getMessage(), repositoryProblem);
        }

        if (loadedUser == null) {
            throw new InternalAuthenticationServiceException(
                    "UserDetailsService returned null, which is an interface contract violation");
        }
        return loadedUser;
    }

拿到用户信息之后,回到AbstractUserDetailsAuthenticationProvider

try {
            preAuthenticationChecks.check(user);
            additionalAuthenticationChecks(user,
                    (UsernamePasswordAuthenticationToken) authentication);
                    //在dao里校验密码是否匹配
        }

它会预检查用户是否过期,是否禁用,之后检查密码是否匹配。
预检查之后还会有后置检查

postAuthenticationChecks.check(user);  //

所有检查都通过,就会认为用户的认证是成功的。

return createSuccessAuthentication(principalToReturn, authentication, user);

protected Authentication createSuccessAuthentication(Object principal,
            Authentication authentication, UserDetails user) {
        UsernamePasswordAuthenticationToken result = new UsernamePasswordAuthenticationToken(
                principal, authentication.getCredentials(),
                authoritiesMapper.mapAuthorities(user.getAuthorities()));
        result.setDetails(authentication.getDetails());

        return result;
    }

再次new 了UsernamePasswordAuthenticationToken对象,区别再与构造方法不同,传递的参数不同,这个时候权限,用户信息都已经拿到

2.认证结果如何在多个请求之间共享

3.获取用户认证的信息

原文地址:http://blog.51cto.com/mazongfei/2335750

时间: 2024-10-05 12:42:52

串理spring security认证流程源码的相关文章

Spring Security教程(八):用户认证流程源码详解

本篇文章主要围绕下面几个问题来深入源码: 用户认证流程 认证结果如何在多个请求之间共享 获取认证用户信息 一.用户认证流程 上节中提到Spring Security核心就是一系列的过滤器链,当一个请求来的时候,首先要通过过滤器链的校验,校验通过之后才会访问用户各种信息. 这里要说明的是在过滤器的最前端有一个SecurityContextPersistenceFilter,当请求进来和返回的时候都会经过这个过滤器,它主要存放用户的认证信息.这里先简单提一下,后面会详解. 当用户发送登录请求的时候(

SpringSecurity 默认表单登录页展示流程源码

SpringSecurity 默认表单登录页展示流程源码 本篇主要讲解 SpringSecurity提供的默认表单登录页 它是如何展示的的流程, 涉及 1.FilterSecurityInterceptor, 2.ExceptionTranslationFilter , 3.DefaultLoginPageGeneratingFilter 过滤器, 并且简单介绍了 AccessDecisionManager 投票机制 ?1.准备工作(体验SpringSecurity默认表单认证) ??1.1 创

Activity启动流程源码分析之Launcher启动(二)

1.前述 在前一篇文章中我们简要的介绍Activity的启动流程Activity启动流程源码分析之入门(一),当时只是简单的分析了一下流程,而且在上一篇博客中我们也说了Activity的两种启动方式,现在我们就来分析其中的第一种方式--Launcher启动,这种启动方式的特点是会创建一个新的进程来加载相应的Activity(基于Android5.1源码). 2.Activity启动流程时序图 好啦,接下来我们先看一下Launcher启动Activity的时序图: 好啦,接下来我们将上述时序图用代

JobTracker启动流程源码级分析

org.apache.hadoop.mapred.JobTracker类是个独立的进程,有自己的main函数.JobTracker是在网络环境中提交及运行MR任务的核心位置. main方法主要代码有两句: 1 //创建jobTracker对象 2 JobTracker tracker = startTracker(new JobConf()); 3 //启动各个服务,包括JT内部一些重要的服务或者线程 4 tracker.offerService(); 一.startTracker(new Jo

Android指令处理流程源码追踪

1.拨号界面输入*#06#,显示IMEI号,这是怎么出来的? 2.如何快速的找出Android平台中的指令? 1. 在DialpadFragment中的EditText注册一个Textchanged的监听器TextWatcher, 当EditText中的内容发生变化时会调用相应的回调函数,TextWatcher是一个接口: public interface TextWatcher extends NoCopySpan { public void beforeTextChanged(CharSeq

A2dp初始化流程源码分析

蓝牙启动的时候,会涉及到各个profile 的启动.这篇文章分析一下,蓝牙中a2dp profile的初始化流程. 我们从AdapterState.java中对于USER_TURN_ON 消息的处理说起: switch(msg.what) { case USER_TURN_ON: notifyAdapterStateChange(BluetoothAdapter.STATE_TURNING_ON); mPendingCommandState.setTurningOn(true); transit

Spring Security认证简介

认证简介 认证过程 用户使用用户名和密码进行登录. Spring Security 将获取到的用户名和密码封装成一个实现了 Authentication 接口的 UsernamePasswordAuthenticationToken. 将上述产生的 token 对象传递给 AuthenticationManager 进行登录认证. AuthenticationManager 认证成功后将会返回一个封装了用户权限等信息的 Authentication 对象. 通过调用 SecurityContex

Spring Security 认证过程

目录 1.1     认证过程 1.2     Web应用的认证过程 1.2.1    ExceptionTranslationFilter 1.2.2    在request之间共享SecurityContext 1.1     认证过程 1.用户使用用户名和密码进行登录. 2.Spring Security将获取到的用户名和密码封装成一个实现了Authentication接口的UsernamePasswordAuthenticationToken. 3.将上述产生的token对象传递给Aut

5.Spark Streaming流计算框架的运行流程源码分析2

1 spark streaming 程序代码实例 代码如下: [html] view plain copy object OnlineTheTop3ItemForEachCategory2DB { def main(args: Array[String]){ val conf = new SparkConf() //创建SparkConf对象 //设置应用程序的名称,在程序运行的监控界面可以看到名称 conf.setAppName("OnlineTheTop3ItemForEachCategor