因爲本人文筆很差,寫不出文風華麗的博客文章而懊惱不已,爲此準備狂讀三千道藏,嘻嘻嘿嘿!好吧,閒話很少聊,直接進入今天的主題吧。java
直接上代碼:git
package org.apache.shiro.web.filter.mgt; import org.apache.shiro.config.ConfigurationException; import javax.servlet.Filter; import javax.servlet.FilterChain; import java.util.Map; import java.util.Set; /** *A FilterChainManager manages the creation and modification of Filter chains from an available pool of Filter instances. *這個FilterChainManager管理來自過濾器實例池的過濾器鏈的修改和建立。 */ public interface FilterChainManager { Map<String, Filter> getFilters(); NamedFilterList getChain(String chainName); boolean hasChains(); Set<String> getChainNames(); void addFilter(String name, Filter filter); void addFilter(String name, Filter filter, boolean init); void createChain(String chainName, String chainDefinition); void addToChain(String chainName, String filterName); void addToChain(String chainName, String filterName, String chainSpecificFilterConfig) throws ConfigurationException; }
看到上面的代碼,對於有過web開發經驗的同窗來講,很快就心照不宣的明白各個方法應該具有怎麼樣的功能。可是小白者居多啊,我仍是挑幾個方法講一下吧,由於的確有幾個方法得注意一下,後文會用到。github
首先:getFilters()與getChain(String chainName)方法有啥區別?一個是獲得過濾器的集合,另外一個是根據chainName獲得相應的過濾器鏈。web
getFilters():咱們知道,每一個過濾器都要有本身的一個名字name,之後根據名字找到相應的過濾器。在shiro框架中,FilterChainManager 建立實例的時候會預先添加一些默認的過濾器。以下:spring
(anon,AnonymousFilter) (authc,FormAuthenticationFilter) (authcBasic,BasicHttpAuthenticationFilter) (logout,LogoutFilter) (noSessionCreation,NoSessionCreationFilter) (perms,PermissionsAuthorizationFilter) (port,PortFilter) (rest,HttpMethodPermissionFilter) (roles,RolesAuthorizationFilter) (ssl,SslFilter) (user,UserFilter)
Map裏面預先裝下了這麼些默認的過濾器,前面的是過濾器的名字,如:anon是過濾器AnonymousFilter的名字,authc是FormAuthenticationFilter的名字,等。隨着項目工程繼續日後執行的過程當中,會往這個Map裏面添加自定義的過濾器。請看spring-config-shiro.xml的部分代碼,以下:express
<!-- 基於Form表單的身份驗證過濾器 --> <bean id="formAuthenticationFilter" class="org.apache.shiro.web.filter.authc.FormAuthenticationFilter"/> <bean id="sysUserFilter" class="com.github.zhangkaitao.shiro.chapter16.web.shiro.filter.SysUserFilter"/> <!-- Shiro的Web過濾器 --> <bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean"> <property name="securityManager" ref="securityManager"/> <property name="loginUrl" value="/login"/> <property name="filters"> <util:map> <entry key="authc" value-ref="formAuthenticationFilter"/> <entry key="sysUser" value-ref="sysUserFilter"/> </util:map> </property> <property name="filterChainDefinitions"> <value> /login = authc /logout = logout /authenticated = authc /** = user,sysUser </value> </property> </bean>
如上代碼可看出,自定義了兩個filter,一個是名字爲authc的FormAuthenticationFilter過濾器,另外一個是名字爲sysUser的SysUserFilter的過濾器。apache
getChain(String chainName):根據chainName獲得NamedFilterList,這個NamedFilterList其實就是一個List集合,裏面專門用來存一個或者多個filter的。那麼chainName是啥呢?用來幹啥的呢?不要怕,由我慢慢道來。其實我在上面貼出的代碼裏,已經用到了chainName的概念。以下圖:數組
從上圖更加直觀的看出這些請求路徑就是chainName。對應的過濾器(一個或者多個)會放進對應的NamedFilterList集合中。框架
接下來再來說講createChain()方法:官方給出的解釋以下this
Creates a filter chain for the given chainName with the specified chainDefinition String. Conventional Use Because the FilterChainManager interface does not impose any restrictions on filter chain names, (it expects only Strings), a convenient convention is to make the chain name an actual URL path expression (such as an Ant path expression). For example: createChain(path_expression, path_specific_filter_chain_definition); This convention can be used by a FilterChainResolver to inspect request URL paths against the chain name (path) and, if a match is found, return the corresponding chain for runtime filtering. Chain Definition Format The chainDefinition method argument is expected to conform to the following format: filter1[optional_config1], filter2[optional_config2], ..., filterN[optional_configN] where 1.filterN is the name of a filter previously registered with the manager, and 2.[optional_configN] is an optional bracketed string that has meaning for that particular filter for this particular chain If the filter does not need specific config for that chain name/URL path, you may discard the brackets - that is, filterN[] just becomes filterN. And because this method does create a chain, remember that order matters! The comma-delimited filter tokens in the chainDefinition specify the chain's execution order. Examples /account/** = authcBasic This example says "Create a filter named '/account/**' consisting of only the 'authcBasic' filter". Also because the authcBasic filter does not need any path-specific config, it doesn't have any config brackets []. /remoting/** = authcBasic, roles[b2bClient], perms["remote:invoke:wan,lan"] This example by contrast uses the 'roles' and 'perms' filters which do use bracket notation. This definition says: Construct a filter chain named '/remoting/**' which 1.ensures the user is first authenticated (authcBasic) then 2.ensures that user has the b2bClient role, and then finally 3.ensures that they have the remote:invoke:lan,wan permission. Note: because elements within brackets [ ] can be comma-delimited themselves, you must quote the internal bracket definition if commas are needed (the above example has 'lan,wan'). If we didn't do that, the parser would interpret the chain definition as four tokens: 1.authcBasic 2.roles[b2bclient] 3.perms[remote:invoke:lan 4.wan] which is obviously incorrect. So remember to use quotes if your internal bracket definitions need to use commas.
好吧,我來翻譯一下。鄙人英語水品有限,不免出錯,懇求各位諒解並留下你寶貴的糾錯。
給給定的帶有chainDefinition字符串的chainName建立一個過濾器鏈。 FilterChainManager接口沒有強加一些限制在過濾器鏈的名字上,指望的就僅僅是字符串。一個方便的約定就是讓chain name符合url路徑的表達式,好比Ant風格的路徑表達式。 這種約定可以讓FilterChainResolver 去檢查請求的url路徑與chain name做比較,若是匹配上了,則返回對應的過濾器鏈。 chainDefinition方法的參數要符合接下來的格式: filter1[optional_config1], filter2[optional_config2], ..., filterN[optional_configN]。 這個"filterN"是先前已經在FilterChainManager註冊過的過濾器的名字。 這個[optional_configN]是可選的帶有括號的字符串給那些特定的字符串在這個過濾器鏈上。 若是這個過濾器沒有指定的配置在這個chain name的url路徑上,那麼filterN[]就等同於filterN。 由於這個方法建立過濾器鏈,記住這個順序的重要性。 在chain definition上的逗號分隔的過濾器令牌指定了過濾器鏈的執行順序。 我本身都感受到翻譯的一塌糊塗,直接看例子吧) 好比:/login = authc,role[devil]。 "/login"就是chain name前面已經說過了,"="號後面的就是chainDefinition,也就是"authc,role[devil]",逗號把各個過濾器隔離開來了。 用String的split方法獲得String數組。String[0]=authc,String[1]=role[devil],因此根據官方文檔給出的過濾器鏈執行順序就是先執行名字爲authc的過濾器,第二個執行名字爲role的過濾器。 你們注意到了role[devil],括號裏面帶有參數devil,在shiro框架中的稱呼是chainSpecificFilterConfig。 如今舉個例子,假如這個過濾器是處理角色功能的,那麼就只有devil角色的用戶纔能有權利訪問該路徑下的資源。 note:在這裏擴從一個知識點,在自定義的過濾器中,若是你想使用這種帶括號的功能,如role[devil],則你自定義的過濾器必須實現PathConfigProcessor接口
FilterChainManager是一個抽象接口,在web開發中,shiro框架中已經提供了實現方案。
DefaultFilterChainManager實現了這個接口。
這個FilterChainManager接口介紹就先到這裏。下面來探討一下它的初始化流程。
在前面的一篇博客文章中:shiro隨筆(1:web.xml中的過濾器配置裏,spring是如何關聯shiro的)已經提到過ShiroFilterFactoryBean,沒錯就是由它來初始化FilterChainManager的,畢竟它是shiro的入口,那麼確定是由它來負責的。找到該類的createFilterChainManager()方法,以下圖:
在shiro隨筆(1:web.xml中的過濾器配置裏,spring是如何關聯shiro的)博客文章中講過,
org.springframework.web.filter.DelegatingFilterProxy會通過層層調用,
直到調用到org.apache.shiro.spring.web.ShiroFilterFactoryBean的getObject()方法,來看一下這個方法
public Object getObject() throws Exception { if (instance == null) { instance = createInstance(); } return instance; }
接下來回去調用createInstance()方法:
好了,今天就寫到這裏了,哇塞,已經晚上11點了,該洗洗睡覺了。各位至今還奮鬥在深夜的同行們,祝你們保重龍體!若是這篇文章可以幫助到你們,歡迎轉載並註明出處。