一、Spring 的DelegatingFilterProxy如何发现 Shiro 的ShiroFilterFactoryBean
简单的回顾一下,web.xml配置中的Spring DelegatingFilterProxy 的这个Filter是如何找到WebApplicationcontext 配置(Spring.xml配置文件)中的ShiroFilterFactoryBean。
web.xml配置:
<filter>
<filter-name>securityFilter</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
<init-param>
<param-name>targetFilterLifecycle</param-name>
<param-value>true</param-value>
</init-param>
</filter>
其实际是通过上面web.xml文件中的<filter-name>securityFilter</filter-name>,securityFilter去找到spring-context.xml配置文件中类型为Filter,id为securityFilter的Bean。
spring-context.xml配置文件:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.0.xsd">
<!-- 此处省略部分配置-->
<bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
<!-- 此处省略部分配置-->
<property name="securityManager" ref="securityManager" />
<property name="unauthorizedUrl" value="/" />
<property name="filters">
<map>
<entry key="formAuthc" value-ref="formAuthc" />
</map>
</property>
<property name="filterChainDefinitions">
<value>
/static/** = anon
/login = formAuthc
/logout = logout
</value>
</property>
</bean>
</beans>
详解的源码分析可见http://my.oschina.net/u/1421030/blog/729706
二、Shiro 的ShiroFilterFactoryBean源码分析
前一节说到Spring的DelegatingFilterProxy是通过在Spring的配置文件中找到类型为Filter且id与web.xml文件其filter-name一致的Bean来发现ShiroFilterFactoryBean的。 但我们会发现ShiroFilterFactoryBean好像没有实现Filter接口,是不是有什么问题呢??
ShiroFilterFactoryBean 声明代码:
public class ShiroFilterFactoryBean implements FactoryBean, BeanPostProcessor...
其实ShiroFilterFactoryBean 实现了FactoryBean接口正式在这里有我们想要的。 ApplicationContext的对待FactoryBean类型的Bean,通过配置文件的中Bean的id得到的其实是FactoryBean#getObject方法对应类型的Bean详细可见http://my.oschina.net/u/1421030/blog/729908。 现在来看看ShiroFilterFactoryBean 的getObject方法的具体实现吧。
ShiroFilterFactoryBean #getObject代码:
public Object getObject() throws Exception {
if (instance == null) {
instance = createInstance();
}
return instance;
}
再看看
ShiroFilterFactoryBean #createInstance代码:
protected AbstractShiroFilter createInstance() throws Exception {
logdebug("Creating Shiro Filter instance.");
SecurityManager securityManager = getSecurityManager();
//省略部分代码
//创建FilterChain管理器,会将Shiro默认的Filter加入进来,同时将配置文件中的Filter加进来
FilterChainManager manager = createFilterChainManager();
PathMatchingFilterChainResolver chainResolver = new PathMatchingFilterChainResolver();
chainResolver.setFilterChainManager(manager);
return new SpringShiroFilter((WebSecurityManager) securityManager, chainResolver);
}
可以看到createInstance方法返回的是一个AbstractShiroFilter 类对象,而该类的父类实际实现了Filter接口。 再来看看createInstance方法中最重要的createFilterChainManager
ShiroFilterFactoryBean #createFilterChainManager代码:
protected FilterChainManager createFilterChainManager() {
/*创建默认的FilterChainManager,创建时会将anon, authc, authcBasic等Filter加入*/
DefaultFilterChainManager manager = new DefaultFilterChainManager();
Map<String, Filter> defaultFilters = manager.getFilters();
//apply global settings if necessary:
for (Filter filter : defaultFilters.values()) {
applyGlobalPropertiesIfNecessary(filter);
}
//得到Spring配置文件中设置给ShiroFilterFactoryBean 的Filter
Map<String, Filter> filters = getFilters();
/*省略部分代码,此处将Spring设置给ShiroFilterFactoryBean 的Filter
加入DefaultFilterChainManager
*/
/*得到配置文件中ShiroFilterFactoryBean 对应的filterChainDefinitions属性设置的键值对
最终能根据vaule找到其对应的其实类Filter的全名与对应的FilterConfig信息
*/
Map<String, String> chains = getFilterChainDefinitionMap();
if (!CollectionUtils.isEmpty(chains)) {
for (Map.Entry<String, String> entry : chains.entrySet()) {
String url = entry.getKey();
String chainDefinition = entry.getValue();
manager.createChain(url, chainDefinition);
}
}
return manager;
}
先看看 DefaultFilterChainManager manager = new DefaultFilterChainManager()
DefaultFilterChainManager构造函数代码
public DefaultFilterChainManager() {
this.filters = new LinkedHashMap<String, Filter>();
this.filterChains = new LinkedHashMap<String, NamedFilterList>();
//加入Shiro中默认的Filter
addDefaultFilters(false);
}
DefaultFilterChainManager#addDefaultFilters代码
protected void addDefaultFilters(boolean init) {
for (DefaultFilter defaultFilter : DefaultFilter.values()) {
addFilter(defaultFilter.name(), defaultFilter.newInstance(), init, false);
}
}
DefaultFilter代码
public enum DefaultFilter {
anon(AnonymousFilter.class),
authc(FormAuthenticationFilter.class),
authcBasic(BasicHttpAuthenticationFilter.class),
logout(LogoutFilter.class),
//省略部分代码
}
到这里终于看到了为什么我们可在spring-context.xml中的ShiroFilterFactoryBean定义中使用anon, authc,authcBasic等去设置filterChainDefinitions啦。 spring-context.xml配置文件:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.0.xsd">
<!-- 此处省略部分配置-->
<bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
<!-- 此处省略部分配置-->
<property name="securityManager" ref="securityManager" />
<property name="unauthorizedUrl" value="/" />
<property name="filters">
<map>
<entry key="formAuthc" value-ref="formAuthc" />
</map>
</property>
<property name="filterChainDefinitions">
<value>
/static/** = anon
/login = formAuthc
/logout = logout
</value>
</property>
</bean>
</beans>
下面一些过程后面的文章分析。