【Shiro学习】FilterChainManager源码分析

FilterChainManager是shiro的Filter链管理器,主要的功能包括2个:

1.维护filter列表:维护shiro的默认filter,并维护用户配置的filter,统一管理

2.路径映射:为每一个filter配置过滤路径chainName和对应的过滤配置chainSpecificFilterConfig。并把同一个请求chainName对应的filter组装成filterList。

3.代理过滤链:让请求先经过shiro的过滤链,然后才执行原来的过滤链。

FilterChainManager的默认实现是DefaultFilterChainManager,首先看一下它的变量。

public class DefaultFilterChainManager implements FilterChainManager {

    private FilterConfig filterConfig;//过滤器配置

    //过滤器资源池,维护filter和filterName,,例如authc对应FormAuthenticationFilter
    private Map<String, Filter> filters; 
    
    // 请求路径chainName和对应filter列表,例如/deleteUser,配置了authc,roles,那么过滤器FormAuthenticationFilter和RolesAuthorizationFilter就组成filterList,处理该请求。
    private Map<String, NamedFilterList> filterChains; 
}

1.维护filter列表

DefaultFilterChainManager在构造函数中,会把shiro默认的过滤器添加到filters。

public DefaultFilterChainManager() {
        this.filters = new LinkedHashMap<String, Filter>();
        this.filterChains = new LinkedHashMap<String, NamedFilterList>();
        // 添加shiro默认的过滤器。
        addDefaultFilters(false);
}
protected void addDefaultFilters(boolean init) {
        for (DefaultFilter defaultFilter : DefaultFilter.values()) {
            addFilter(defaultFilter.name(), defaultFilter.newInstance(), init, false);
        }
}

除此之外,还可以调用addFilter方法把用户配置的filter添加进来,统一管理。

protected void addFilter(String name, Filter filter, boolean init, boolean overwrite) {
        Filter existing = getFilter(name);
        if (existing == null || overwrite) {
            if (filter instanceof Nameable) {
                ((Nameable) filter).setName(name);
            }
            if (init) {
                initFilter(filter);
            }
            this.filters.put(name, filter);
        }
    }

2.路径映射

@Bean
public ShiroFilterFactoryBean shiroFilter2(final SecurityManager securityManager) {
	ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
	shiroFilterFactoryBean.setSecurityManager(securityManager);
	Map<String, String> filterChainDefinitionMap = new HashMap<String, String>();
	filterChainDefinitionMap.put("/getUser", "authc,roles[admin]");
	shiroFilterFactoryBean.setLoginUrl("/login2.html");
	shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
	return shiroFilterFactoryBean;
}

如上图所示,shiro给“/getUser”,配置了“authc,roles[admin]”,那么当用户请求  “/getUser”时候,shiro会使用FormAuthenticationFilter验证登陆,并调用RolesAuthorizationFilter验证权限。那么shiro是如何根据/getUser找到对应的filter?

答案就是:DefaultFilterChainManager会为每一个filter配置过滤路径chainName和对应的过滤配置chainSpecificFilterConfig并把同一个请求chainName对应的filter组装成filterList。

例如shiro给“/getUser”,配置了“authc,roles[admin]”,那么shiroFilter会调用DefaultFilterChainManager.createChain,来构造filterChains.  createChain首先会把“authc,roles[admin]”分割成{“authc”,"roles[admin]"}数组,每一个元素对应一个filter和相应的配置。然后继续分割每一个元素,把每一个元素分割成filterName和chainFilterConfig,例如上面的元素2 “roles[admin]”会被分割为过滤器名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 + "]");
        }

        //parse the value by tokenizing it to get the resulting filter-specific config entries
        //
        //e.g. for a value of
        //
        //     "authc, roles[admin,user], perms[file:edit]"
        //
        // the resulting token array would equal
        //
        //     { "authc", "roles[admin,user]", "perms[file:edit]" }
        // 
        String[] filterTokens = splitChainDefinition(chainDefinition);

        // 每个token都特定于每个筛选器
        for (String token : filterTokens) {
            // 形如roles[admin,user]经过toNameConfigPair会得到 String[0]=roles  
            //string[1]=admin,user
            String[] nameConfigPair = toNameConfigPair(token);
            // 将filter添加到chainName对应的过滤器集合中
            addToChain(chainName, nameConfigPair[0], nameConfigPair[1]);
        }
    }

   DefaultFilterChainManager使用filterChains来维护路径映射。

private Map<String, NamedFilterList> filterChains; //请求路径chainName和对应filter列表

    例如/deleteUser,配置了authc,roles,那么过滤器FormAuthenticationFilter和RolesAuthorizationFilter就组成filterList,处理该请求。

public void addToChain(String chainName, String filterName, String chainSpecificFilterConfig) {
        if (!StringUtils.hasText(chainName)) {
            throw new IllegalArgumentException("chainName cannot be null or empty.");
        }
        Filter filter = getFilter(filterName);
        if (filter == null) {
            throw new IllegalArgumentException("");
        }
        // 配置filter的过滤路径chainName和对应的过滤配置chainSpecificFilterConfig
        applyChainConfig(chainName, filter, chainSpecificFilterConfig);
        // 确保chainName对应的NamedFilterList存在。
        NamedFilterList chain = ensureChain(chainName);
        // 将filter添加到chainName对应的NamedFilterList 中去。
        chain.add(filter);
    }

 如果filter实现了PathConfigProcessor,那么DefaultFilterChainManager会给该filter配置相应的请求路径chainName(例如:/deleteUser),和对应的过滤器配置(例如:”admin,user”)

protected void applyChainConfig(String chainName, Filter filter, String chainSpecificFilterConfig) {
        ...
        if (filter instanceof PathConfigProcessor) {
            // 如果filter实现了PathConfigProcessor,那么将chainName(例如:/deleteUser),和对应的过滤器配置(例如:”admin,user”)赋予对应的filter
            ((PathConfigProcessor) filter).processPathConfig(chainName, chainSpecificFilterConfig);
        } else {
           ...
        }
    }
    protected NamedFilterList ensureChain(String chainName) {
        NamedFilterList chain = getChain(chainName);
        if (chain == null) {
            // 下面会用到SimpleNamedFilterList
            chain = new SimpleNamedFilterList(chainName);
            this.filterChains.put(chainName, chain);
        }
        return chain;
    }

 3.代理过滤链

例如:shiro给“/getUser”,配置了“authc,roles[admin]”,那么当用户请求  “/getUser”时候,shiro会使用FormAuthenticationFilter验证登陆,并调用RolesAuthorizationFilter验证权限。那么shiro是如何把这两个过滤器加入到原来的过滤器链中?,并且顺序是怎样的呢?

答案就是,当请求到达shiroFilter时候,shiroFilter会使用DefaultFilterChainManager来代理原过滤器链,让shiro的过滤器依次执行(根据定义的先后,例如“authc,roles[admin]”,那么authc过滤器先于roles过滤器),然后再执行原来的过滤器链。

这其中关键的步骤就是DefaultFilterChainManager.proxy方法:

    public FilterChain proxy(FilterChain original, String chainName) {
        NamedFilterList configured = getChain(chainName);
        if (configured == null) {
            String msg = "There is no configured chain under the name/key [" + chainName + "].";
            throw new IllegalArgumentException(msg);
        }
        return configured.proxy(original);
    }

由上面可知DefaultFilterChainManager使用SimpleNamedFilterList来装载filter. SimpleNamedFilterList.proxy方法如下:

    public FilterChain proxy(FilterChain orig) {
        return new ProxiedFilterChain(orig, this);
    }

ProxiedFilterChain会先执行自己的filters集合,然后才执行原来的过滤器链。

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 {
        if (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);
        }
    }
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值