Shiro源碼分析---FilterChain建立過程

在Shiro中,不管是認證仍是權限控制都是經過過濾器來實現的,在應用中可能會配置不少個過濾器,但對於不一樣的訪問請求所須要通過的過濾器確定是不同的,那麼當發起一個請求時,到底會應用上哪些過濾器,對於咱們使用Shiro就顯示得格外重要;下面就來說講一個請求到底會通過哪些過濾器。java

在Shiro中,確證一個請求會通過哪些過濾器是經過org.apache.shiro.web.filter.mgt.FilterChainResolver接口來定義的,下面是接口定義:web

public interface FilterChainResolver {

    FilterChain getChain(ServletRequest request, ServletResponse response, FilterChain originalChain);

}

接口中只有一個方法getChain,就是用於肯定請求到底要通過哪些過濾器,而後將這些過濾器封裝成一個FilterChain對象,FilterCahin咱們很熟悉,在使用Servlet的時候常常見面。
FilterChainResolver只是一個接口,Shiro提供了一個默認的實現類org.apache.shiro.web.filter.mgt.PathMatchingFilterChainResolver,該實現類會根據請求路徑進行匹配過濾器。
在看PathMatchingFilterChainResolver源碼以前先說一下FilterChainManager中的FilterChain是怎麼來的,以ini配置爲例:apache

[urls]
/static/**=anon
/formfilterlogin=authc
/role=authc,roles[admin]

其中/static/**/formfilterlogin/role 就是受FilterChainManager管理的FilterChain的名稱。下面看看FilterChainManager是如何管理FilterChain的。
Shiro提供了FilterChainManager一個的默認實現:org.apache.shiro.web.filter.mgt.DefaultFilterChainManager,其createChain方法會在系統啓動的時候被org.apache.shiro.web.config.IniFilterChainResolverFactory調用,用於建立各個FilterChain。下面以/role=authc,roles[admin]配置爲例,chainName就是/rolechainDefinition就是authc,roles[admin]數組

public void createChain(String chainName, String chainDefinition) {
    if (!StringUtils.hasText(chainName)) {
        throw new NullPointerException("chainName cannot be null or empty.");
    }
    if (!StringUtils.hasText(chainDefinition)) {
        throw new NullPointerException("chainDefinition cannot be null or empty.");
    }

    if (log.isDebugEnabled()) {
        log.debug("Creating chain [" + chainName + "] from String definition [" + chainDefinition + "]");
    }

    // filterTokens數組有兩個元素,第一個爲authc,第二個爲roles[admin],由於配置時能夠配置多個Filter,
    // 多個Filter間以逗號分隔
    String[] filterTokens = splitChainDefinition(chainDefinition);

    for (String token : filterTokens) {
        // 對roles[admin]進行分隔,數組中第一個元素爲roles,第二個爲admin
        String[] nameConfigPair = toNameConfigPair(token);

        // 添加FilterChain
        addToChain(chainName, nameConfigPair[0], nameConfigPair[1]);
    }
}
public void addToChain(String chainName, String filterName, String chainSpecificFilterConfig) {
    if (!StringUtils.hasText(chainName)) {
        throw new IllegalArgumentException("chainName cannot be null or empty.");
    }

    // 根據filterName(role)查找出Filter
    Filter filter = getFilter(filterName);
    if (filter == null) {
        throw new IllegalArgumentException("There is no filter with name '" + filterName +
                "' to apply to chain [" + chainName + "] in the pool of available Filters.  Ensure a " +
                "filter with that name/path has first been registered with the addFilter method(s).");
    }
    // 應用FilterChain配置,以roles[amdin]爲例,調用該方法後roles過濾器就知道其進行攔截器須要admin角色
    applyChainConfig(chainName, filter, chainSpecificFilterConfig);

    // 若是chainName之前沒處理過則建立一個新的NamedFilterList對象,若是處理過則返回之前的NamedFilterList對象
    // 因此在FilterChainManager中,存儲Filter的是NamedFilterList對象
    NamedFilterList chain = ensureChain(chainName);
    // 將過濾器添加至鏈中
    chain.add(filter);
}

在瞭解了FilterChainManager是如何建立與存儲FilterChain之後,再來看看FilterChainResolver是如何肯定一個請求須要通過哪些過濾器的。app

public FilterChain getChain(ServletRequest request, ServletResponse response, FilterChain originalChain) {
    FilterChainManager filterChainManager = getFilterChainManager();
    // 判斷FilterChainManager中是否有FilterChain,若是沒有則返回null
    if (!filterChainManager.hasChains()) {
        return null;
    }
    // 獲取請求URI
    String requestURI = getPathWithinApplication(request);

    // FilterChain的名稱就是路徑匹配符,若是請求URI匹配上了某個FilterChain 
    // 則調用FilterChainManager.proxy方法返回一個FilterChain對象,注意是返回第一個匹配FilterChain
    // 也就是說若是在ini配置文件中配置了多個同名的FilterChain,則只有第一個FilterChain有效
    for (String pathPattern : filterChainManager.getChainNames()) {
        if (pathMatches(pathPattern, requestURI)) {
            if (log.isTraceEnabled()) {
                log.trace("Matched path pattern [" + pathPattern + "] for requestURI [" + requestURI + "].  " +
                        "Utilizing corresponding filter chain...");
            }
            return filterChainManager.proxy(originalChain, pathPattern);
        }
    }

    return null;
}

下面是DefualtFilterChainManager.proxy方法源碼:ide

public FilterChain proxy(FilterChain original, String chainName) {
    // 路徑模式匹配(如/static/**)就是FilterChain名稱
    // 根據FilterChain名稱查找NamedFilterList對象(存儲了配置的Filter)
    NamedFilterList configured = getChain(chainName);
    if (configured == null) {
        String msg = "There is no configured chain under the name/key [" + chainName + "].";
        throw new IllegalArgumentException(msg);
    }
    // 調用NamedFilterList.proxy方法
    return configured.proxy(original);
}

NamedFilterList的實現類爲org.apache.shiro.web.filter.mgt.SimpleNamedFilterListthis

public FilterChain proxy(FilterChain orig) {
    // 返回ProxiedFilterChain對象,該對象就是當一個請求到來後須要被執行的FilterChain對象
    // 該對象只是一個代理對象,代理了兩個FilterChain,一個是NamedFilterList,另外一個是原始的FilterChain對象
    // 原始的FilterChain對象包含了在web.xml中配置並應用上的Filter
    return new ProxiedFilterChain(orig, this);
}
public class ProxiedFilterChain implements FilterChain {

    private static final Logger log = LoggerFactory.getLogger(ProxiedFilterChain.class);

    private FilterChain orig;
    private List<Filter> filters;
    private int index = 0;

    public ProxiedFilterChain(FilterChain orig, List<Filter> filters) {
        if (orig == null) {
            throw new NullPointerException("original FilterChain cannot be null.");
        }
        this.orig = orig;
        this.filters = filters;
        this.index = 0;
    }

    public void doFilter(ServletRequest request, ServletResponse response) throws IOException, ServletException {
        // 能夠看出,先執行原始Filter,再執行NamedFilterList中的Filter
        if (this.filters == null || this.filters.size() == this.index) {
            //we've reached the end of the wrapped chain, so invoke the original one:
            if (log.isTraceEnabled()) {
                log.trace("Invoking original filter chain.");
            }
            this.orig.doFilter(request, response);
        } else {
            if (log.isTraceEnabled()) {
                log.trace("Invoking wrapped filter at index [" + this.index + "]");
            }
            this.filters.get(this.index++).doFilter(request, response, this);
        }
    }
}

至此,Shiro建立FilterChain過程講解完畢,若有錯誤之處,盡請指正。url

-------------------------------- END -------------------------------debug

及時獲取更多精彩文章,請關注公衆號《Java精講》。代理

相關文章
相關標籤/搜索