AI智能
改变未来

Shiro源码分析—FilterChain创建过程

在Shiro中,无论是认证还是权限控制都是通过过滤器来实现的,在应用中可能会配置很多个过滤器,但对于不同的访问请求所需要经过的过滤器肯定是不一样的,那么当发起一个请求时,到底会应用上哪些过滤器,对于我们使用Shiro就显示得格外重要;下面就来讲讲一个请求到底会经过哪些过滤器。

在Shiro中,确证一个请求会经过哪些过滤器是通过

org.apache.shiro.web.filter.mgt.FilterChainResolver

接口来定义的,下面是接口定义:

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配置为例:

[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,第二个为adminString[] nameConfigPair = toNameConfigPair(token);// 添加FilterChainaddToChain(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)查找出FilterFilter 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

是如何确定一个请求需要经过哪些过滤器的。

public FilterChain getChain(ServletRequest request, ServletResponse response, FilterChain originalChain) {FilterChainManager filterChainManager = getFilterChainManager();// 判断FilterChainManager中是否有FilterChain,如果没有则返回nullif (!filterChainManager.hasChains()) {return null;}// 获取请求URIString 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

方法源码:

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

public FilterChain proxy(FilterChain orig) {// 返回ProxiedFilterChain对象,该对象就是当一个请求到来后需要被执行的FilterChain对象// 该对象只是一个代理对象,代理了两个FilterChain,一个是NamedFilterList,另一个是原始的FilterChain对象// 原始的FilterChain对象包含了在web.xml中配置并应用上的Filterreturn 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中的Filterif (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

过程讲解完毕,如有错误之处,尽请指正。

——————————– END ——————————-

及时获取更多精彩文章,请关注公众号《Java精讲》。

赞(0) 打赏
未经允许不得转载:爱站程序员基地 » Shiro源码分析—FilterChain创建过程