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);
}
}
}