Spring Security原理之springSecurityFilterChain

在上篇文章中,已经大概交代了一下三个地方

spring security启动WebSecurityConfiguration主要做了两件事情:
1.根据WebSecurityConfigurerAdapter中配置的信息创建WebSecurity这个类
2.springSecurityFilterChain()创建了一个名叫springSecurityFilterChain的过滤器,然后值得一提的是在调用WebSecuritybuild()创建过滤器的时候,调用到了WebSecurityinit()方法创建了一个HttpSecurity的对象,这里会根据配置为我们创建过滤器,最后添加到DefaultSecurityFilterChain过滤器链里面来

1.为什么要说这个类

在前面的文章中我们已经说到所有的过滤器链都添加到了securityFilterChains这个列表中,后面出现了下面一行代码

FilterChainProxy filterChainProxy = new FilterChainProxy(securityFilterChains);

我们知道FilterChainProxy的bean的名字叫做springSecurityFilterChain里面包含了我们的过滤器链
所以我们必须要清楚他的工作原理

2.类的简单说明

2.1变量

2.1.1 filterChains

变量定义

private List<SecurityFilterChain> filterChains
  • 里面存储了过滤器链的信息

2.2构造函数

public FilterChainProxy(List<SecurityFilterChain> filterChains) {
        this.filterChains = filterChains;
    }

WebSecurityperformBuild()方法中我们对于spring security的配置最后生成的过滤器链设置到filterChains 这个变量中

3.虚拟过滤器链VirtualFilterChain

private static class VirtualFilterChain implements FilterChain {
                 
                //原始过滤器链,代表了spring 的application context中所有的过滤器连
               //其实可以参考DelegatingFilterProxy
        private final FilterChain originalChain;
               
                //spring security的过滤器链
        private final List<Filter> additionalFilters;
                
               //因为在FilterChainProxy中request和response被封装到HttpFirewall中,所以我们从这里获取请求信息
        private final FirewalledRequest firewalledRequest;
        
             //spring security的过滤器的长度
              private final int size;
            
           //当前执行到的位置
        private int currentPosition = 0;

        private VirtualFilterChain(FirewalledRequest firewalledRequest,
                FilterChain chain, List<Filter> additionalFilters) {
            this.originalChain = chain;
            this.additionalFilters = additionalFilters;
            this.size = additionalFilters.size();
            this.firewalledRequest = firewalledRequest;
        }

        @Override
        public void doFilter(ServletRequest request, ServletResponse response)
                throws IOException, ServletException {
                        //表示spring security的过滤器全部执行完成,那么可以执行spring的application context 中的其他的过滤器链了
            if (currentPosition == size) {
                if (logger.isDebugEnabled()) {
                    logger.debug(UrlUtils.buildRequestUrl(firewalledRequest)
                            + " reached end of additional filter chain; proceeding with original chain");
                }

                // Deactivate path stripping as we exit the security filter chain
                this.firewalledRequest.reset();

                originalChain.doFilter(request, response);
            }
            else {
                               //获取当前过滤器执行到的为止
                currentPosition++;
                               
                               //当前需要执行的过滤器
                Filter nextFilter = additionalFilters.get(currentPosition - 1);

                if (logger.isDebugEnabled()) {
                    logger.debug(UrlUtils.buildRequestUrl(firewalledRequest)
                            + " at position " + currentPosition + " of " + size
                            + " in additional filter chain; firing Filter: '"
                            + nextFilter.getClass().getSimpleName() + "'");
                }

                               //执行当前的过滤器
                nextFilter.doFilter(request, response, this);
            }
        }
    }

4 FilterChainProxydoFilter方法

我们知道FilterChainProxy已经被注册到application context中,那么就会被DelegatingFilterProxy执行到自己的doFilter()方法,然而先没必要想这么多,就把FilterChainProxy当成我们在web.xml中定义的一个Filter就行,当有请求进来就会去执行doFilter方法

@Override
    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);
                doFilterInternal(request, response, chain);
            }
            finally {
                SecurityContextHolder.clearContext();
                request.removeAttribute(FILTER_APPLIED);
            }
        }
        else {
            doFilterInternal(request, response, chain);
        }
    }

    private void doFilterInternal(ServletRequest request, ServletResponse response,
            FilterChain chain) throws IOException, ServletException {

        FirewalledRequest fwRequest = firewall
                .getFirewalledRequest((HttpServletRequest) request);
        HttpServletResponse fwResponse = firewall
                .getFirewalledResponse((HttpServletResponse) response);

        List<Filter> filters = getFilters(fwRequest);

        if (filters == null || filters.size() == 0) {
            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);

            return;
        }
               VirtualFilterChain vfc = new VirtualFilterChain(fwRequest, chain, filters);
        vfc.doFilter(fwRequest, fwResponse);
    }
  • 上面这么多行的代码其实我们大概捋顺一下就知道,主要的无非就是当有请求,判断当前过滤器列表里面是否有过滤器,如果没有执行spring application context中的其他的过滤器链,如果有执行一下两行代码
VirtualFilterChain vfc = new VirtualFilterChain(fwRequest, chain, filters);
vfc.doFilter(fwRequest, fwResponse);
  • 首先新建了一个虚拟过滤器链,请参考上面的代码
  • 其次执行了doFilter()方法
  • 根据上面对于VirtualFilterChain的源代码注释,我们是可以看到过滤器链是怎样被执行完的。

5.总结

这个类还是比较简单和清晰明了的。没有晦涩的继承关系。到此为止,我们已经了解到了spring security 的一个请求过来为什么会去穿透我们的过滤器,可以说对于spring security的启动,spring security filter chain的组建,我们基本已经了解完成,最后,在默认情况下进行调试,我们看一下我们的默认的过滤器有哪些,也顺便结束这篇文章吧.

img

默认配置下的过滤器

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值