• SpringSecurity - 启动流程分析(三)



    活动地址:CSDN21天学习挑战赛

    承上启下

    SpringSecurity - 启动流程分析(二) 这篇文章中,我们分析了 HttpSecurity 是如何转换为 List,并最终放入 DefaultSecurityFilterChain 的。

    // HttpSecurity 的 build() 方法
    @Override
    protected DefaultSecurityFilterChain performBuild() {
    	filters.sort(comparator);
    	return new DefaultSecurityFilterChain(requestMatcher, filters);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    WebSecuritybuild() 方法在之后的逻辑中把 DefaultSecurityFilterChain 添加到了 List 中,然后把 List 作为参数 new 了一个 FilterChainProxy 类作为方法返回对象

    for (SecurityBuilder<? extends SecurityFilterChain> securityFilterChainBuilder : securityFilterChainBuilders) {
    	// 这里的 securityFilterChainBuilder 就是 HttpSecurity,build() 方法返回了 DefaultSecurityFilterChain
    	securityFilterChains.add(securityFilterChainBuilder.build());
    }
    FilterChainProxy filterChainProxy = new FilterChainProxy(securityFilterChains);
    ...
    Filter result = filterChainProxy;
    ...
    return result;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    也就是说 SpringSecurity - 启动流程分析(一) 中的核心配置类 WebSecurityConfiguration 中的核心流程: springSecurityFilterChain() 方法返回的是一个 FilterChainProxy(又回到最初的起点)。

    @Bean(name = AbstractSecurityWebApplicationInitializer.DEFAULT_FILTER_NAME)
    public Filter springSecurityFilterChain() throws Exception {
    	boolean hasConfigurers = webSecurityConfigurers != null
    			&& !webSecurityConfigurers.isEmpty();
    	if (!hasConfigurers) {
    		WebSecurityConfigurerAdapter adapter = objectObjectPostProcessor
    				.postProcess(new WebSecurityConfigurerAdapter() {
    				});
    		webSecurity.apply(adapter);
    	}
    	return webSecurity.build();
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    到此为止,我们知道了:

    • @EnableWebSecurity 注解为 IoC 容器 中添加了 WebSecurityConfiguration 配置类
    • WebSecurityConfiguration 配置类为容器中添加了 springSecurityFilterChain Bean
    • springSecurityFilterChain Bean 是一个类型为 FilterFilterChainProxy
    • FilterChainProxy 中包含了 DefaultSecurityFilterChain 过滤器链
    • DefaultSecurityFilterChain 过滤器链中是由 HttpSecurity 转化而来的 Filters

    SpringBoot - 配置 Filter 的几种方式 文章中我们知道,在容器中添加一个 Filter 类型的 Bean,就会拦截所有请求,但是从上面代码中我们看到返回的是一个 FilterChainProxy

    从源码中可以看到,继承了 GenericFilterBean,关于 GenericFilterBean 可以查看 SpringMVC - 对于如何配置 Filter 的深度剖析 这篇文章

    public class FilterChainProxy extends GenericFilterBean {
        private static final Log logger = LogFactory.getLog(FilterChainProxy.class);
        private static final String FILTER_APPLIED = FilterChainProxy.class.getName().concat(".APPLIED");
        // 这里就是传进来的 DefaultSecurityFilterChain,当然还包含了其他的 SecurityFilterChain
        private List<SecurityFilterChain> filterChains;
        ...
    	
        public FilterChainProxy(List<SecurityFilterChain> filterChains) {
            this.filterChainValidator = new FilterChainProxy.NullFilterChainValidator();
            this.firewall = new StrictHttpFirewall();
            // 过滤器链,可以打印一下看看里面都有啥
            this.filterChains = filterChains;
        }
    
        public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
            boolean clearContext = request.getAttribute(FILTER_APPLIED) == null;
            if (clearContext) {
                try {
                    request.setAttribute(FILTER_APPLIED, Boolean.TRUE);
                    this.doFilterInternal(request, response, chain);
                } finally {
                    SecurityContextHolder.clearContext();
                    request.removeAttribute(FILTER_APPLIED);
                }
            } else {
                this.doFilterInternal(request, response, chain);
            }
    
        }
    
        private void doFilterInternal(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
            FirewalledRequest fwRequest = this.firewall.getFirewalledRequest((HttpServletRequest)request);
            HttpServletResponse fwResponse = this.firewall.getFirewalledResponse((HttpServletResponse)response);
            List<Filter> filters = this.getFilters((HttpServletRequest)fwRequest);
            if (filters != null && filters.size() != 0) {
            	// 虚拟过滤器链
                FilterChainProxy.VirtualFilterChain vfc = new FilterChainProxy.VirtualFilterChain(fwRequest, chain, filters);
                vfc.doFilter(fwRequest, fwResponse);
            } else {
                if (logger.isDebugEnabled()) {
                    logger.debug(UrlUtils.buildRequestUrl(fwRequest) + (filters == null ? " has no matching filters" : " has an empty filter list"));
                }
    
                fwRequest.reset();
                chain.doFilter(fwRequest, fwResponse);
            }
        }
    
        ...
    
        private static class VirtualFilterChain implements FilterChain {
        	// Servlet 容器中的原始过滤器链
            private final FilterChain originalChain;
            // SpringSecurity 的过滤器链
            private final List<Filter> additionalFilters;
            private final FirewalledRequest firewalledRequest;
            private final int size;
            private int currentPosition;
    
            private VirtualFilterChain(FirewalledRequest firewalledRequest, FilterChain chain, List<Filter> additionalFilters) {
                this.currentPosition = 0;
                this.originalChain = chain;
                this.additionalFilters = additionalFilters;
                this.size = additionalFilters.size();
                this.firewalledRequest = firewalledRequest;
            }
    
            public void doFilter(ServletRequest request, ServletResponse response) throws IOException, ServletException {
            	// SpringSecurity 中的 Filter 执行完之后,接着执行 Servlet 容器中的 Filter
                if (this.currentPosition == this.size) {
                    if (FilterChainProxy.logger.isDebugEnabled()) {
                        FilterChainProxy.logger.debug(UrlUtils.buildRequestUrl(this.firewalledRequest) + " reached end of additional filter chain; proceeding with original chain");
                    }
    
                    this.firewalledRequest.reset();
                    this.originalChain.doFilter(request, response);
                } else {
                	// 执行 SpringSecurity 中的 Filter,知道执行完为止
                    ++this.currentPosition;
                    Filter nextFilter = (Filter)this.additionalFilters.get(this.currentPosition - 1);
                    if (FilterChainProxy.logger.isDebugEnabled()) {
                        FilterChainProxy.logger.debug(UrlUtils.buildRequestUrl(this.firewalledRequest) + " at position " + this.currentPosition + " of " + this.size + " in additional filter chain; firing Filter: '" + nextFilter.getClass().getSimpleName() + "'");
                    }
    
                    nextFilter.doFilter(request, response, this);
                }
    
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90

    项目启动时 Debug 看一下 FilterChainProxy 中的 filterChains 会被初始化为什么:

    在这里插入图片描述

    当有请求进来时,Debug 看一下 VirtualFilterChain 构造完成后包含的参数:

    在这里插入图片描述

    可以看到,正如上面所分析的,additionalFilters 中是 SpringSecurity 中的 Filters,而 originalChainServlet 容器中的过滤器链,也就是 ApplicationFilterChain

    在这里插入图片描述

    这里的 ApplicationFilterConfig 在之后的 Tomcat 学习 专栏再进行分析,这里主要是理解 SpringSecurity 的过滤逻辑

    在这里插入图片描述

  • 相关阅读:
    LeetCode 636. 函数的独占时间
    【Python计算机视觉】Python全栈体系(二十六)
    【图像配准】基于SURF特征实现印刷体汉字配准附matlab代码
    面向AI时代的软件开发新范式
    偏向锁,轻量级锁,重量级锁的核心原理
    安装 torch_geometric
    Django常见面试题总结(二)
    保研笔记四 软件工程与计算卷二(8-12章)
    2.20 day2 QT
    蓝桥杯练习题十三 - 猜字母(c++)
  • 原文地址:https://blog.csdn.net/qiaohao0206/article/details/126126318