03 spring security执行流程分析

spring security主要是依赖一系列的Filter来实现权限验证的,责任链设计模式是跑不了的。下面简单记录一下spring操作这些Filter的过程。

1. WebSecurityConfiguration.java

该类是spring security的一个配置类,里面定了一系列的Bean,咱主要是看springSecurityFilterChain这个bean, 就是它创建了FilterChain.

    @Bean(name = AbstractSecurityWebApplicationInitializer.DEFAULT_FILTER_NAME)
    public Filter springSecurityFilterChain() throws Exception {
        // 通过webSecurity来构建了FilterChain
        return webSecurity.build();
    }    

2. AbstractConfiguredSecurityBuilder.java

在该类中就加载我们配置权限规则,以及spring security默认的一系列Filter, 权限规则就是01 02篇中我们自定义的SecurityConfig.java类,示例代码如下:

@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    /**
     *  配置的权限规则
     */
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        // todo 

    }
}    

再看看AbstractConfiguredSecurityBuilder.java中部分源码,就是在这段代码里面加载了屁多的东西,暂时还没看明白。。。

  @Override
    protected final O doBuild() throws Exception {
        synchronized (configurers) {
            buildState = BuildState.INITIALIZING;

            beforeInit();

            init();

            buildState = BuildState.CONFIGURING;

            beforeConfigure();
         //
            configure();

            buildState = BuildState.BUILDING;

            //
            O result = performBuild();

            buildState = BuildState.BUILT;

            return result;
        }
    }   

3. SecurityContextPersistenceFilter.java

This filter will only execute once per request, to resolve servlet container(specifically Weblogic) incompatibilities.该filter每个请求只执行一次,主要是解决servlet容器的兼容性问题。
This filter MUST be executed BEFORE any authentication processing mechanisms.必须在权限认证之前执行
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)throws IOException, ServletException {
        HttpServletRequest request = (HttpServletRequest) req;
        HttpServletResponse response = (HttpServletResponse) res;

        if (request.getAttribute(FILTER_APPLIED) != null) {
            // ensure that filter is only applied once per request
            chain.doFilter(request, response);
            return;
        }

        request.setAttribute(FILTER_APPLIED, Boolean.TRUE);

        // 设置security的上下文context
        HttpRequestResponseHolder holder = new HttpRequestResponseHolder(request,response);
        SecurityContext contextBeforeChainExecution = repo.loadContext(holder);

        try {
            SecurityContextHolder.setContext(contextBeforeChainExecution);
            // 开始执行chain上的filter
            chain.doFilter(holder.getRequest(), holder.getResponse());

        }
        finally {
            // 清除security的上下文context
            SecurityContext contextAfterChainExecution = SecurityContextHolder.getContext();
            SecurityContextHolder.clearContext();
            repo.saveContext(contextAfterChainExecution, holder.getRequest(),holder.getResponse());
            request.removeAttribute(FILTER_APPLIED);
        }
    }

由上面的debug截图可知,FilterSecurityInterceptor.java是链上最后一个Filter,它会对权限进行验证

InterceptorStatusToken token = super.beforeInvocation(fi);

就是在它父类AbstractSecurityInterceptor#beforeInvocation(fi)方法中,进行权限验证,且继续看代码

这儿抛异常会被它前一个Filter捕获,也就是会被ExceptionTranslationFilter.java捕获。

在上面的异常处理中,如果没有权限就会进行重定向到登录页面

下面进入LoginUrlAuthenticationEntryPoint#commence(HttpServletRequest request, HttpServletResponse response,AuthenticationException authException)

4. 登录页面

提交请求之后,又会执行整个filterchain, 此次我们重点分析UsernamePasswordAuthenticationFilter.java, 因为,使用Form表登录,会在该类进行权限验证,下面来看看具体的逻辑

5. UsernamePasswordAuthenticationFilter.java

 该类主要就是进行权限验证

    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());
        }
        // 从request对象中获取username , password
        String username = obtainUsername(request);
        String password = obtainPassword(request);

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

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

        username = username.trim();

        // 创建一个token,该token对象持有用户信息
        UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(username, password);

        // 获取用登录的ip, session相关的信息
        setDetails(request, authRequest);

        return this.getAuthenticationManager().authenticate(authRequest);
    }

debug看一下return this.getAuthenticationManager().authenticate(authRequest)之前的情况

接着,看看this.getAuthenticationManager().authenticate(authRequest)该方法。

好, 下面进入AuthenticationManager接口的具体实现ProviderManager#authenticate(Authentication authentication)方法

跟踪代码,我们最终会看到这样一个方法DaoAuthenticationProvider#retrieveUser(String username,UsernamePasswordAuthenticationToken authentication)

protected final UserDetails retrieveUser(String username,UsernamePasswordAuthenticationToken authentication)
            throws AuthenticationException {
        prepareTimingAttackProtection();
        try {
            // this.getUserDetailsService() 这个就是我们实现UserDetailsService接口的MyUserDetailsService.java
            UserDetails loadedUser = this.getUserDetailsService().loadUserByUsername(username);
            if (loadedUser == null) {
                throw new InternalAuthenticationServiceException("UserDetailsService returned null, which is an interface contract violation");
            }
            return loadedUser;
        }
        catch (UsernameNotFoundException ex) {
            mitigateAgainstTimingAttack(authentication);
            throw ex;
        }
        catch (InternalAuthenticationServiceException ex) {
            throw ex;
        }
        catch (Exception ex) {
            throw new InternalAuthenticationServiceException(ex.getMessage(), ex);
        }
    }

该方法返回UserDetails 对象后,又会进入到AbstractUserDetailsAuthenticationProvider#authenticate(Authentication authentication)方法,然后对UserDetails 对象做些后置检测,

比如,账号是否锁定,是否过期,是否可用。。。

在最最后面,spring secutiry会再次创建一个UsernamePasswordAuthenticationToken对象的token,不过此次与前面创建的不同,前面创建的token中只有username和password ,

此次会调用UsernamePasswordAuthenticationToken 三个参数的构造器

    public UsernamePasswordAuthenticationToken(Object principal, Object credentials,
            Collection<? extends GrantedAuthority> authorities) {
        super(authorities);
        this.principal = principal;
        this.credentials = credentials;
        super.setAuthenticated(true); // must use super, as we override
    }

好,暂时就到这儿吧

原文地址:https://www.cnblogs.com/z-qinfeng/p/11783672.html

时间: 2024-08-29 20:59:24

03 spring security执行流程分析的相关文章

Spring Batch_JOB执行流程分析

debug 代码 JobExecution result = launcher.run(job, jobParametersBuilder.toJobParameters()); 这是启动job的方法,如下是方法的具体实现: SimpleJobLauncher.java run方法的具体实现(删除了部分代码) @Override public JobExecution run(final Job job, final JobParameters jobParameters) throws Job

Spring MVC 执行流程分析

Spring MVC 的执行流程图 原文地址:https://www.cnblogs.com/wbyixx/p/10290491.html

转 深入浅出Mybatis系列(十)---SQL执行流程分析(源码篇)

深入浅出Mybatis系列(十)---SQL执行流程分析(源码篇) 最近太忙了,一直没时间继续更新博客,今天忙里偷闲继续我的Mybatis学习之旅.在前九篇中,介绍了mybatis的配置以及使用, 那么本篇将走进mybatis的源码,分析mybatis 的执行流程, 好啦,鄙人不喜欢口水话,还是直接上干活吧: 1. SqlSessionFactory 与 SqlSession. 通过前面的章节对于mybatis 的介绍及使用,大家都能体会到SqlSession的重要性了吧, 没错,从表面上来看,

Java Servlet(十二):Servlet、Listener、Filter之间的执行流程分析

时隔几年后,看到本系列文章讲解的内容缺少了不少内容:周末无事分析了Spring Security是如何被集成到Web Servlet(SpringMVC)时,需要重新理清Filter.Listener.Servlet(SpringMVC#DispatcherServlet)之间的执行顺序,于是就有了本篇文章.这个话题是Web Servlet学习中的一个重点,弄清它们之间的执行流程,有助于理解SpringMVC.Spring Security这些框架是否如何与Web Servlet集成到一起. 原

spring security源码分析之二---web包分析

Spring 是一个非常流行和成功的 Java 应用开发框架.Spring Security 基于 Spring 框架,提供了一套 Web 应用安全性的完整解决方案.一般来说,Web 应用的安全性包括用户认证(Authentication)和用户授权(Authorization)两个部分.用户认证指的是验证某个用户是否为系统中的合法主体,也就是说用户能否访问该系统.用户认证一般要求用户提供用户名和密码.系统通过校验用户名和密码来完成认证过程.用户授权指的是验证某个用户是否有权限执行某个操作.在一

Spring Core Container 源码分析三:Spring Beans 初始化流程分析

前言 本文是笔者所著的 Spring Core Container 源码分析系列之一: 本篇文章主要试图梳理出 Spring Beans 的初始化主流程和相关核心代码逻辑: 本文转载自本人的私人博客,伤神的博客: http://www.shangyang.me/2017/04/01/spring-core-container-sourcecode-analysis-beans-instantiating-process/ 本文为作者的原创作品,转载需注明出处: 源码分析环境搭建 参考 Sprin

Hive SQL执行流程分析

转自 http://www.tuicool.com/articles/qyUzQj 最近在研究Impala,还是先回顾下Hive的SQL执行流程吧. Hive有三种用户接口: cli (Command line interface) bin/hive或bin/hive –service cli 命令行方式(默认) hive-server/hive-server2 bin/hive –service hiveserver 或bin/hive –service hiveserver2 通过JDBC/

wget www.baidu.com执行流程分析

通过GDB分析程序的执行流程如下: main.c(main) url_parse:解析url,获取url相关信息,返回结构体 struct url 的指针,存于 url_parsed retrieve_url:主要参数 url_parsed ,下载文件,下载网页的关键函数 retr.c(retrieve_url) http_loop,通过 HTTP 下载指定文件 http.c(http_loop) gethttp, 获取文档 http.c(gethttp) connect_to_host:给定域

ThinkPHP 框架执行流程分析

总体来说,应用的流程涉及到几个文件:Index.phpThinkPHP.phpThink.class.phpApp.class.phpDispatcher.class.phpThinkPHP/Mode/common.phpReadHtmlBehavior.class.phpRoute.class.phpHook.class.phpContentReplaceBehavior.class.phpWriteHtmlCacheBehavior.class.php ThinkPHP框架开发的应用的标准执