Spring Security4.0.3源碼分析之FilterChainProxy執行過程分析

最近在學習安全框架spring Security,想弄清楚其中實現的具體步驟,於是下定決心,研究一下Spring Security源碼,這篇博客的目的是想把學習過程記錄下來。學習過程中主要參考了http://dead-knight.iteye.com/blog/1511389大神的博客,然後在其基礎上,進行更詳細的說明

在類org.springframework.web.filter.DelegatingFilterProxy的doFilter方法中可以看到,過濾器執行流程實際上是讓委託執行實際的doFilter操作

@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain) throws ServletException, IOException {
    Filter delegateToUse = this.delegate;
    if (delegateToUse == null) {
        synchronized (this.delegateMonitor) {
            if (this.delegate == null) {
                WebApplicationContext wac = findWebApplicationContext();
                if (wac == null) {
                    throw new IllegalStateException("No WebApplicationContext found: no ContextLoaderListener registered?");
                }
                // 初始化springSecurityFilterChain,詳解請移步2.3
                this.delegate = initDelegate(wac);
            }
            delegateToUse = this.delegate;
        }
    }

    // 讓委託執行實際的doFilter操作
    invokeDelegate(delegateToUse, request, response, filterChain);
}

通過前面幾篇博客的分析,我們也知道這裏的delegateToUse實際上是org.springframework.security.web.FilterChainProxy

接下來看FilterChainProxy中的doFilter執行流程

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();
        // 執行默認過濾器如編碼過濾器EncodingFilter,轉換請求方式過濾器HiddenHttpMethodFilter等
        chain.doFilter(fwRequest, fwResponse);
        return;
    }

    // 把實際doFilter任務交給VirtualFilterChain處理  
    VirtualFilterChain vfc = new VirtualFilterChain(fwRequest, chain, filters);
    vfc.doFilter(fwRequest, fwResponse);
}

// VirtualFilterChain的doFilter方法
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();

        // 執行Filter的doFilter操作
        originalChain.doFilter(request, response);
    } else {
        // 當前位置加一
        currentPosition++;

        // 根據當前位置從過濾器列表中取出一個Filter
        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() + "'");
        }

        // 執行Filte r的doFilter操作
        // 把自身作爲參數傳遞給doFilter方法,這樣doFilter方法最後會調用VirtualFilterChain的doFilter方法,這樣控制就又回到了VirtualFilterChain
        nextFilter.doFilter(request, response, this);
    }
}

下一篇博客將介紹各個過濾器的具體功能

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章