在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
就是/role
,chainDefinition
就是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.SimpleNamedFilterList
this
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精講》。代理