lihongjie0209 / myblog

4 stars 0 forks source link

Spring Security: FilterChainProxy #180

Open lihongjie0209 opened 4 years ago

lihongjie0209 commented 4 years ago

Configuration

As of version 3.1, FilterChainProxy is configured using a list of SecurityFilterChain instances, each of which contains a RequestMatcher and a list of filters which should be applied to matching requests. Most applications will only contain a single filter chain, and if you are using the namespace, you don't have to set the chains explicitly. If you require finer-grained control, you can make use of the namespace element. This defines a URI pattern and the list of filters (as comma-separated bean names) which should be applied to requests which match the pattern. An example configuration might look like this:

    <bean id="myfilterChainProxy" class="org.springframework.security.web.FilterChainProxy">
        <constructor-arg>
            <util:list>
                <security:filter-chain pattern="/do/not/filter*" filters="none"/>
                <security:filter-chain pattern="/**" filters="filter1,filter2,filter3"/>
            </util:list>
        </constructor-arg>
    </bean>

The names "filter1", "filter2", "filter3" should be the bean names of Filter instances defined in the application context. The order of the names defines the order in which the filters will be applied. As shown above, use of the value "none" for the "filters" can be used to exclude a request pattern from the security filter chain entirely. Please consult the security namespace schema file for a full list of available configuration options.

lihongjie0209 commented 4 years ago

整体的设计

image

image

lihongjie0209 commented 4 years ago

核心代码

    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);
    }

首先是进行HTTP防火墙, 防止一些恶意的请求

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

然后获取配置的 filterChain, 第一个匹配的发现了就直接使用

    private List<Filter> getFilters(HttpServletRequest request) {
        for (SecurityFilterChain chain : filterChains) {
            if (chain.matches(request)) {
                return chain.getFilters();
            }
        }

        return null;
    }

如果没有对于当前请求没有配置额外的安全过滤链, 那么清除防火墙的状态, 继续原来的过滤链


        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);

而这个虚拟的过滤链是优先使用我们定义的过滤链


@Override
        public void doFilter(ServletRequest request, ServletResponse response)
                throws IOException, ServletException {
            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);
            }
        }