Spring与Shiro整合源码分析

拈花ヽ惹草 提交于 2020-03-02 16:38:45

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()了。

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!