Shiro随笔(2:探讨FilterChainManager)

由于本人文笔不好,写不出文风华丽的博客文章而懊恼不已,为此准备狂读三千道藏,嘻嘻嘿嘿!好吧,闲话不多聊,直接进入今天的主题吧。

直接上代码:


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开发经验的同学来说,很快就心领神会的明白各个方法应该具备怎么样的功能。但是小白者居多啊,我还是挑几个方法讲一下吧,因为的确有几个方法得注意一下,后文会用到。

首先:getFilters()与getChain(String chainName)方法有啥区别?一个是得到过滤器的集合,另一个是根据chainName得到相应的过滤器链。

getFilters():我们知道,每个过滤器都要有自己的一个名字name,以后根据名字找到相应的过滤器。在shiro框架中,FilterChainManager 创建实例的时候会预先添加一些默认的过滤器。如下:

(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的部分代码,如下:

    <!-- 基于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的过滤器。

getChain(String chainName):根据chainName得到NamedFilterList,这个NamedFilterList其实就是一个List集合,里面专门用来存一个或者多个filter的。那么chainName是啥呢?用来干啥的呢?不要怕,由我慢慢道来。其实我在上面贴出的代码里,已经用到了chainName的概念。如下图:

从上图更加直观的看出这些请求路径就是chainName。对应的过滤器(一个或者多个)会放进对应的NamedFilterList集合中。

接下来再来讲讲createChain()方法:官方给出的解释如下

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点了,该洗洗睡觉了。各位至今还奋斗在深夜的同行们,祝大家保重龙体!如果这篇文章能够帮助到大家,欢迎转载并注明出处。

转载于:https://my.oschina.net/u/2381372/blog/1204706

已标记关键词 清除标记
表情包
插入表情
评论将由博主筛选后显示,对所有人可见 | 还能输入1000个字符 “速评一下”
©️2020 CSDN 皮肤主题: 大白 设计师:CSDN官方博客 返回首页