Spring与Shiro整合是通过在web.xml里面配置过滤器:
<filter>
<filter-name>shiroFilter</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
<init-param>
<param-name>targetFileterLifecycle</param-name>
<param-value>true</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>shiroFilter</filter-name>
<url-pattern>/*</url-pattern>
<dispatcher>REQUEST</dispatcher>
<dispatcher>FORWARD</dispatcher>
<dispatcher>INCLUDE</dispatcher>
<dispatcher>ERROR</dispatcher>
</filter-mapping>
然后我们在apllicationContext.xml里面配置如下的ShiroFilterFactoryBean就可以了
<!-- Shiro 的Web过滤器 -->
<bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
<property name="securityManager" ref="securityManager" />
<property name="loginUrl" value="/login" />
<property name="unauthorizedUrl" value="/unauth" />
<property name="filters">
<map>
<entry key="authc" value-ref="formAuthenticationFilter" />
</map>
</property>
<property name="filterChainDefinitions">
<value>
/login = anon
/unauth= anon
/logout = logout
/class/**=authc,roles[3]
/account/** = authc,roles[1]
/** = authc,user
</value>
</property>
</bean>
为什么这样配置就可以了?我们并没有在web.xml配置shiro的filter啊,但是又确实进入到了shiro的OncePerRequestFilter,它里面有和java web完全一样的doFilter(ServletRequest, ServletResponse, FilterChain)方法
public final void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
String alreadyFilteredAttributeName = getAlreadyFilteredAttributeName();
if ( request.getAttribute(alreadyFilteredAttributeName) != null ) {
filterChain.doFilter(request, response);
} else if ( !isEnabled(request, response) || shouldNotFilter(request) ) {
filterChain.doFilter(request, response);
} else {
request.setAttribute(alreadyFilteredAttributeName, Boolean.TRUE);
try {
doFilterInternal(request, response, filterChain);
} finally {
// Once the request has finished, we're done and we don't
// need to mark as 'already filtered' any more.
request.removeAttribute(alreadyFilteredAttributeName);
}
}
}
上面的疑惑要从DelegatingFilterProxy说起,DelegatingFilterProxy继承了GenericFilterBean,而GenericFilterBean实现了Filter接口,在DelegatingFilterProxy实例化完成后会调用filter的init(FilterConfig filterConfig)进行一些初始化操作,也就是最开始调用GenericFilterBean的init方法,代码如下
public final void init(FilterConfig filterConfig) throws ServletException {
//省略
initFilterBean();
}
init方法里面最重要的就是调用其子类DelegatingFilterProxy的initFilterBean方法初始化我们在apllicationContext.xml里面配置的ShiroFilterFactoryBean。
protected void initFilterBean() throws ServletException {
synchronized (this.delegateMonitor) {
if (this.delegate == null) {
// If no target bean name specified, use filter name.
if (this.targetBeanName == null) {
this.targetBeanName = getFilterName();
}
// Fetch Spring root application context and initialize the delegate early,
// if possible. If the root application context will be started after this
// filter proxy, we'll have to resort to lazy initialization.
WebApplicationContext wac = findWebApplicationContext();
if (wac != null) {
this.delegate = initDelegate(wac);
}
}
}
}
首先判断delegate是否存在,如果不存在再看targetBeanName是否为空,如果也为空就要执行this.targetBeanName = getFilterName();,而这个值是从web.xml里面得到的,如何得到的呢?
我们看getFilterName()方法的实现就能一目了然,知道shiro究竟怎么和spring整合在一起。
protected final String getFilterName() {
return (this.filterConfig != null ? this.filterConfig.getFilterName() : this.beanName);
}
看到了吧他是获取的在web.xml中配置的DelegatingFilterProxy这个filter的filterName,而我们在那里配置的fileterName的值为shiroFilter,也就是targetBeanName的值为shiroFilter了,和applicationContext.xml里面配置ShiroFilterFactoryBean这个bean的id一样,是不是有点关系了。
如果我们把在web.xml中配置的DelegatingFilterProxy这个filter的filterName改为其他的比如"shiroFilteraa",那么当启动tomcat时会发现报错了,spring找不到name为“shiroFilteraa的bean”.
到目前为止获取到了targetBeanName的值为shiroFilter,而shiroFilter它是一个Filter那么就要调用它的init(FilterConfig config)方法初始化,这就是initFilterBean()这个方法中this.delegate = initDelegate(wac);的作用了。
进入initDelegate(wac)的实现:
protected Filter initDelegate(WebApplicationContext wac) throws ServletException {
Filter delegate = wac.getBean(getTargetBeanName(), Filter.class);
if (isTargetFilterLifecycle()) {
delegate.init(getFilterConfig());
}
return delegate;
}
这里首先获取name为shiroFilter的bean,并且他的类型必须是Filter,然后判断targetFilterLifecycle属性是false还是true,决定是否调用该类的init方法。
getTargetName()的值是shiroFilter,而这个bean对应的是ShiroFilterFactoryBean,它并不是一个filter,但是getBean()要求返回的Bean又必须是Filter类型的,那怎么得到的呢?
跟进去Debug,经过很多次Debug才搞清楚
debug。。。。
首先获得ShiroFilterFactoryBean这个Bean,这个Bean是在AbstractBeanFactory这个类的doGetBean方法得到的,如下图:
doGetBean方法的实现如下:
protected <T> T doGetBean(
final String name, final Class<T> requiredType, final Object[] args, boolean typeCheckOnly)
throws BeansException {
final String beanName = transformedBeanName(name);
Object bean;
// Eagerly check singleton cache for manually registered singletons.
Object sharedInstance = getSingleton(beanName);
if (sharedInstance != null && args == null) {
//省略
bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
}
//省略
}
Object sharedInstance = getSingleton(beanName);这一行代码就是获得真正的根据shiroFilter这个name获得ShiroFilterFactoryBean这个实例对象,里面有配置的shiro的各种参数。
接下来就是最关键的了,bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);这行代码返回真正需要的SpringShiroFilter,就是它实现了Filter接口,它是shiro的入口。
接下来看getObjectForBeanInstance()方法的具体实现,进到AbstractBeanFactory.getObjectForBeanInstance()方法中:
protected Object getObjectForBeanInstance(
Object beanInstance, String name, String beanName, RootBeanDefinition mbd) {
//省略部分代码
Object object = null;
if (mbd == null) {
object = getCachedObjectForFactoryBean(beanName);
}
if (object == null) {
// Return bean instance from factory.
FactoryBean<?> factory = (FactoryBean<?>) beanInstance;
// Caches object obtained from FactoryBean if it is a singleton.
if (mbd == null && containsBeanDefinition(beanName)) {
mbd = getMergedLocalBeanDefinition(beanName);
}
boolean synthetic = (mbd != null && mbd.isSynthetic());
object = getObjectFromFactoryBean(factory, beanName, !synthetic);
}
return object;
}
这里面首先把beanInstance(也就是ShiroFilterFactoryBean这个实例对象)强制类型转换为FactoryBean,然后object = getObjectFromFactoryBean(factory, beanName, !synthetic);这一行去得到真正的SpringShiroFilter,最终是这个factory调用其自身的getObject()方法返回SpringShiroFilter,这个调用getObject()方法是在FactoryBeanRegistrySupport的doGetObjectFromFactoryBean()执行的。
private Object doGetObjectFromFactoryBean(final FactoryBean<?> factory, final String beanName)
throws BeanCreationException {
Object object;
try {
if (System.getSecurityManager() != null) {
//省略部分代码
}
else {
object = factory.getObject(); //获得真正的SpringShiroFilter
}
}
return object;
}
接下来就是调用getObject()方法了, 前面说过这个factory就是ShiroFilterFactoryBean这个实例对象,进到ShiroFilterFactoryBean.getObject()方法去看,getObject()方法实现如下,可以看到最终的SpringShiroFilter这个对象是createInstance()方法new出来的。
public Object getObject() throws Exception {
if (instance == null) {
instance = createInstance();
}
return instance;
}
protected AbstractShiroFilter createInstance() throws Exception {
log.debug("Creating Shiro Filter instance.");
SecurityManager securityManager = getSecurityManager();
if (securityManager == null) {
String msg = "SecurityManager property must be set.";
throw new BeanInitializationException(msg);
}
if (!(securityManager instanceof WebSecurityManager)) {
String msg = "The security manager does not implement the WebSecurityManager interface.";
throw new BeanInitializationException(msg);
}
FilterChainManager manager = createFilterChainManager();
//Expose the constructed FilterChainManager by first wrapping it in a
// FilterChainResolver implementation. The AbstractShiroFilter implementations
// do not know about FilterChainManagers - only resolvers:
PathMatchingFilterChainResolver chainResolver = new PathMatchingFilterChainResolver();
chainResolver.setFilterChainManager(manager);
//Now create a concrete ShiroFilter instance and apply the acquired SecurityManager and built
//FilterChainResolver. It doesn't matter that the instance is an anonymous inner class
//here - we're just using it because it is a concrete AbstractShiroFilter instance that accepts
//injection of the SecurityManager and FilterChainResolver:
return new SpringShiroFilter((WebSecurityManager) securityManager, chainResolver);
}
看到了吧,最后return new SpringShiroFilter()了。
来源:oschina
链接:https://my.oschina.net/u/927844/blog/746882