Shiro随笔(2:探讨FilterChainManager)

因为本人文笔很差,写不出文风华丽的博客文章而懊恼不已,为此准备狂读三千道藏,嘻嘻嘿嘿!好吧,闲话很少聊,直接进入今天的主题吧。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点了,该洗洗睡觉了。各位至今还奋斗在深夜的同行们,祝你们保重龙体!若是这篇文章可以帮助到你们,欢迎转载并注明出处。

相关文章
相关标签/搜索