Shiro创建FilterChain过程详解

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

 

在Shiro中,确证一个请求会经过哪些过滤器是通过org.apache.shiro.web.filter.mgt.FilterChainResolver接口来定义的,下面是接口定义:

 

[java] view plain copy

  1. public interface FilterChainResolver {  
  2.   
  3.     FilterChain getChain(ServletRequest request, ServletResponse response, FilterChain originalChain);  
  4.   
  5. }  


接口中只有一个方法getChain,就是用于确定请求到底要经过哪些过滤器,然后将这些过滤器封装成一个FilterChain对象,FilterCahin我们很熟悉,在使用Servlet的时候经常见面。

FilterChainResolver只是一个接口,Shiro提供了一个默认的实现类org.apache.shiro.web.filter.mgt.PathMatchingFilterChainResolver,该实现类会根据请求路径进行匹配过滤器。

在看PathMatchingFilterChainResolver源码之前先说一下FilterChainManager中的FilterChain是怎么来的,以ini配置为例:

 

 

 

 

[plain] view plain copy

  1. [urls]  
  2. /static/**=anon  
  3. /formfilterlogin=authc  
  4. /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]

 

 

 

 

[java] view plain copy

  1. public void createChain(String chainName, String chainDefinition) {  
  2.     if (!StringUtils.hasText(chainName)) {  
  3.         throw new NullPointerException("chainName cannot be null or empty.");  
  4.     }  
  5.     if (!StringUtils.hasText(chainDefinition)) {  
  6.         throw new NullPointerException("chainDefinition cannot be null or empty.");  
  7.     }  
  8.   
  9.     if (log.isDebugEnabled()) {  
  10.         log.debug("Creating chain [" + chainName + "] from String definition [" + chainDefinition + "]");  
  11.     }  
  12.   
  13.     // filterTokens数组有两个元素,第一个为authc,第二个为roles[admin],因为配置时可以配置多个Filter,  
  14.     // 多个Filter间以逗号分隔  
  15.     String[] filterTokens = splitChainDefinition(chainDefinition);  
  16.   
  17.     for (String token : filterTokens) {  
  18.         // 对roles[admin]进行分隔,数组中第一个元素为roles,第二个为admin  
  19.         String[] nameConfigPair = toNameConfigPair(token);  
  20.   
  21.         // 添加FilterChain  
  22.         addToChain(chainName, nameConfigPair[0], nameConfigPair[1]);  
  23.     }  
  24. }  
  25. public void addToChain(String chainName, String filterName, String chainSpecificFilterConfig) {  
  26.     if (!StringUtils.hasText(chainName)) {  
  27.         throw new IllegalArgumentException("chainName cannot be null or empty.");  
  28.     }  
  29.       
  30.     // 根据filterName(role)查找出Filter  
  31.     Filter filter = getFilter(filterName);  
  32.     if (filter == null) {  
  33.         throw new IllegalArgumentException("There is no filter with name '" + filterName +  
  34.                 "' to apply to chain [" + chainName + "] in the pool of available Filters.  Ensure a " +  
  35.                 "filter with that name/path has first been registered with the addFilter method(s).");  
  36.     }  
  37.     // 应用FilterChain配置,以roles[amdin]为例,调用该方法后roles过滤器就知道其进行拦截器需要admin角色  
  38.     applyChainConfig(chainName, filter, chainSpecificFilterConfig);  
  39.   
  40.     // 如果chainName以前没处理过则创建一个新的NamedFilterList对象,如果处理过则返回以前的NamedFilterList对象  
  41.     // 所以在FilterChainManager中,存储Filter的是NamedFilterList对象  
  42.     NamedFilterList chain = ensureChain(chainName);  
  43.     // 将过滤器添加至链中  
  44.     chain.add(filter);  
  45. }  


在了解了FilterChainManager是如何创建与存储FilterChain以后,再来看看FilterChainResolver是如何确定一个请求需要经过哪些过滤器的。

 

 

[java] view plain copy

  1. public FilterChain getChain(ServletRequest request, ServletResponse response, FilterChain originalChain) {  
  2.     FilterChainManager filterChainManager = getFilterChainManager();  
  3.     // 判断FilterChainManager中是否有FilterChain,如果没有则返回null  
  4.     if (!filterChainManager.hasChains()) {  
  5.         return null;  
  6.     }  
  7.     // 获取请求URI  
  8.     String requestURI = getPathWithinApplication(request);  
  9.   
  10.     // FilterChain的名称就是路径匹配符,如果请求URI匹配上了某个FilterChain   
  11.     // 则调用FilterChainManager.proxy方法返回一个FilterChain对象,注意是返回第一个匹配FilterChain  
  12.     // 也就是说如果在ini配置文件中配置了多个同名的FilterChain,则只有第一个FilterChain有效  
  13.     for (String pathPattern : filterChainManager.getChainNames()) {  
  14.         if (pathMatches(pathPattern, requestURI)) {  
  15.             if (log.isTraceEnabled()) {  
  16.                 log.trace("Matched path pattern [" + pathPattern + "] for requestURI [" + requestURI + "].  " +  
  17.                         "Utilizing corresponding filter chain...");  
  18.             }  
  19.             return filterChainManager.proxy(originalChain, pathPattern);  
  20.         }  
  21.     }  
  22.   
  23.     return null;  
  24. }  


下面是DefualtFilterChainManager.proxy方法源码:

 

 

 

 

[java] view plain copy

  1. public FilterChain proxy(FilterChain original, String chainName) {  
  2.     // 路径模式匹配(如/static/**)就是FilterChain名称  
  3.     // 根据FilterChain名称查找NamedFilterList对象(存储了配置的Filter)  
  4.     NamedFilterList configured = getChain(chainName);  
  5.     if (configured == null) {  
  6.         String msg = "There is no configured chain under the name/key [" + chainName + "].";  
  7.         throw new IllegalArgumentException(msg);  
  8.     }  
  9.     // 调用NamedFilterList.proxy方法  
  10.     return configured.proxy(original);  
  11. }  


NamedFilterList的实现类为org.apache.shiro.web.filter.mgt.SimpleNamedFilterList

 

 

 

 

[java] view plain copy

  1. public FilterChain proxy(FilterChain orig) {  
  2.     // 返回ProxiedFilterChain对象,该对象就是当一个请求到来后需要被执行的FilterChain对象  
  3.     // 该对象只是一个代理对象,代理了两个FilterChain,一个是NamedFilterList,另一个是原始的FilterChain对象  
  4.     // 原始的FilterChain对象包含了在web.xml中配置并应用上的Filter  
  5.     return new ProxiedFilterChain(orig, this);  
  6. }  

 

[java] view plain copy

  1. public class ProxiedFilterChain implements FilterChain {  
  2.   
  3.     private static final Logger log = LoggerFactory.getLogger(ProxiedFilterChain.class);  
  4.   
  5.     private FilterChain orig;  
  6.     private List filters;  
  7.     private int index = 0;  
  8.   
  9.     public ProxiedFilterChain(FilterChain orig, List filters) {  
  10.         if (orig == null) {  
  11.             throw new NullPointerException("original FilterChain cannot be null.");  
  12.         }  
  13.         this.orig = orig;  
  14.         this.filters = filters;  
  15.         this.index = 0;  
  16.     }  
  17.       
  18.       
  19.     public void doFilter(ServletRequest request, ServletResponse response) throws IOException, ServletException {  
  20.         // 可以看出,先执行原始Filter,再执行NamedFilterList中的Filter  
  21.         if (this.filters == null || this.filters.size() == this.index) {  
  22.             //we've reached the end of the wrapped chain, so invoke the original one:  
  23.             if (log.isTraceEnabled()) {  
  24.                 log.trace("Invoking original filter chain.");  
  25.             }  
  26.             this.orig.doFilter(request, response);  
  27.         } else {  
  28.             if (log.isTraceEnabled()) {  
  29.                 log.trace("Invoking wrapped filter at index [" + this.index + "]");  
  30.             }  
  31.             this.filters.get(this.index++).doFilter(request, response, this);  
  32.         }  
  33.     }  
  34. }  


至此,Shiro创建FilterChain过程讲解完毕,如有错误之处,尽请指正。

你可能感兴趣的:(shiro)