在Spring MVC集成Shiro中,最重要的当属这个ShiroFactoryBean了,可以说是开放给开发者的一个入口。通常的开发配置如下所示:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:jaxws="http://cxf.apache.org/jaxws" xmlns:p="http://www.springframework.org/schema/p"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx" xmlns:jdbc="http://www.springframework.org/schema/jdbc"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:util="http://www.springframework.org/schema/util"
xsi:schemaLocation="
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/jdbc http://www.springframework.org/schema/jdbc/spring-jdbc-3.0.xsd
http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-3.0.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd">
<!-- 配置shrio的验证管理 -->
<bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
<property name="realms">
<list>
<ref bean="shiroRealm"/>
</list>
</property>
</bean>
<!-- 配置shrio的验证 -->
<bean id="shiroRealm" class="com.gameloft9.demo.security.ShiroRealm"/>
<!-- 配置shrio的过滤功能 -->
<bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
<!-- shiro的核心安全接口 -->
<property name="securityManager" ref="securityManager"/>
<!-- 要求登录时的链接 -->
<property name="loginUrl" value="/login"/>
<!-- 未授权的链接 -->
<property name="unauthorizedUrl" value="/views/unaouth.jsp"/>
<!-- 过滤器 -->
<property name="filters">
<map>
<entry key="authc">
<bean class="com.gameloft9.demo.shiro.ShiroAuthFilter"/>
</entry>
</map>
</property>
<!-- shiro连接约束配置 -->
<property name="filterChainDefinitions">
<value>
/ImgValidateServlet = anon
/queryPublishContent.do=anon
/login = anon
/*.do = authc
/ = anon
</value>
</property>
</bean>
</beans>
嗯,很常见的配置三部曲,配置SecurityManager,配置Realm,然后配置过滤器和过滤链。让我们对比着看看ShiroFilterFactoryBean的内容:
private SecurityManager securityManager;
private Map<String, Filter> filters = new LinkedHashMap();
private Map<String, String> filterChainDefinitionMap = new LinkedHashMap();
private String loginUrl;
private String successUrl;
private String unauthorizedUrl;
private AbstractShiroFilter instance;
可以看到,xml配置大部分就是在设置这些属性内容,除了最后的那个instance,这个后面再说。在上面的过滤链设置中,我们使用了anon,authc等filter,但是xml配置中我们过滤器只设置了authc。那么其它的过滤器是哪里来的,为什么可以直接使用?
其实Shiro已经内置了一些常用的过滤器,例如:anon,roles,user等等。这些过滤器被定义在一个默认的过滤器枚举里面,如下所示:
package org.apache.shiro.web.filter.mgt;
import org.apache.shiro.util.ClassUtils;
import org.apache.shiro.web.filter.authc.*;
import org.apache.shiro.web.filter.authz.*;
import org.apache.shiro.web.filter.session.NoSessionCreationFilter;
import javax.servlet.Filter;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import java.util.LinkedHashMap;
import java.util.Map;
public enum DefaultFilter {
anon(AnonymousFilter.class),
authc(FormAuthenticationFilter.class),
authcBasic(BasicHttpAuthenticationFilter.class),
logout(LogoutFilter.class),
noSessionCreation(NoSessionCreationFilter.class),
perms(PermissionsAuthorizationFilter.class),
port(PortFilter.class),
rest(HttpMethodPermissionFilter.class),
roles(RolesAuthorizationFilter.class),
ssl(SslFilter.class),
user(UserFilter.class);
private final Class<? extends Filter> filterClass;
private DefaultFilter(Class<? extends Filter> filterClass) {
this.filterClass = filterClass;
}
public Filter newInstance() {
return (Filter) ClassUtils.newInstance(this.filterClass);
}
public Class<? extends Filter> getFilterClass() {
return this.filterClass;
}
}
这些默认的过滤器和用户自定义的过滤器组成了最终的过滤器集合,而它们添加的时机也不一样。在创建ShrioFilterFactoryBean的时候,用户自定义的filter已经被设置到了filters属性里面。这个filtes存放的只有用户添加的,不包含默认的。ShrioFilterFactoryBean创建AbstractShrioFilter实例的时候(也就是我们之前说的那个instance),会创建一个FilterChainManager。如下所示:
protected AbstractShiroFilter createInstance() throws Exception {
log.debug("Creating Shiro Filter instance.");
SecurityManager securityManager = this.getSecurityManager();
String msg;
if(securityManager == null) {
msg = "SecurityManager property must be set.";
throw new BeanInitializationException(msg);
} else if(!(securityManager instanceof WebSecurityManager)) {
msg = "The security manager does not implement the WebSecurityManager interface.";
throw new BeanInitializationException(msg);
} else {
FilterChainManager manager = this.createFilterChainManager();
PathMatchingFilterChainResolver chainResolver = new PathMatchingFilterChainResolver();
chainResolver.setFilterChainManager(manager);
return new ShiroFilterFactoryBean.SpringShiroFilter((WebSecurityManager)securityManager, chainResolver);
}
}
createFilterChainManager()方法里面会调用DefaultFilterChainManager manager = new DefaultFilterChainManager();创建一个默认的实例。现在魔法发生了,在构造函数里面,我们看到了addDefaultFilters方法!没错!这里就是添加默认filters的地方。更里面的内容,大家可以自己看看源码。
public DefaultFilterChainManager() {
this.filters = new LinkedHashMap<String, Filter>();
this.filterChains = new LinkedHashMap<String, NamedFilterList>();
addDefaultFilters(false);
}
然后我们回到createFilterChainManager()方法里面来,创建了默认的filters后,会再去ShrioFilterFactoryBean拿到我们之前自定义好的filter,通过一个for循环把它们一个一个加进去,而且在manager.addFilter里面会覆盖重名的filter。也就是说用户自定义的filter会覆盖掉同名的默认的filter。
protected FilterChainManager createFilterChainManager() {
DefaultFilterChainManager manager = new DefaultFilterChainManager();
Map<String, Filter> defaultFilters = manager.getFilters();
Iterator var3 = defaultFilters.values().iterator();
while(var3.hasNext()) {
Filter filter = (Filter)var3.next();
this.applyGlobalPropertiesIfNecessary(filter);
}
Map<String, Filter> filters = this.getFilters();
String name;
Filter filter;
if(!CollectionUtils.isEmpty(filters)) {
for(Iterator var10 = filters.entrySet().iterator(); var10.hasNext(); manager.addFilter(name, filter, false)) {
Entry<String, Filter> entry = (Entry)var10.next();
name = (String)entry.getKey();
filter = (Filter)entry.getValue();
this.applyGlobalPropertiesIfNecessary(filter);
if(filter instanceof Nameable) {
((Nameable)filter).setName(name);
}
}
}
Map<String, String> chains = this.getFilterChainDefinitionMap();
if(!CollectionUtils.isEmpty(chains)) {
Iterator var12 = chains.entrySet().iterator();
while(var12.hasNext()) {
Entry<String, String> entry = (Entry)var12.next();
String url = (String)entry.getKey();
String chainDefinition = (String)entry.getValue();
manager.createChain(url, chainDefinition);
}
}
return manager;
}
在方法的最后,还添加了我们xml设置的过滤链。这样我们xml配置里面的过滤器和过滤链就完整的被消化到了DefaultFilterChainManager里面去了,然后manager又被托管到PathMatchingFilterChainResolver(其实就是再加了一个正则匹配器)。最后返回了一个spring包装过的ShiroFilter(就是那个instance):
FilterChainManager manager = this.createFilterChainManager();
PathMatchingFilterChainResolver chainResolver = new PathMatchingFilterChainResolver();
chainResolver.setFilterChainManager(manager);
return new ShiroFilterFactoryBean.SpringShiroFilter((WebSecurityManager)securityManager, chainResolver);
核心内容就是我们的securityManager和FilterChainResolver,至此ShiroFilterFactoryBean的工作就做完了!