Spring與Shiro整合源碼分析

Spring與Shiro整合是經過在web.xml裏面配置過濾器:java

<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就能夠了web

<!-- 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)方法spring

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方法,代碼以下apache

public final void init(FilterConfig filterConfig) throws ServletException {
	//省略

	initFilterBean();
}

init方法裏面最重要的就是調用其子類DelegatingFilterProxy的initFilterBean方法初始化咱們在apllicationContext.xml裏面配置的ShiroFilterFactoryBean。tomcat

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裏面獲得的,如何獲得的呢?app

咱們看getFilterName()方法的實現就能一目瞭然,知道shiro究竟怎麼和spring整合在一塊兒。ui

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同樣,是否是有點關係了。this

    若是咱們把在web.xml中配置的DelegatingFilterProxy這個filter的filterName改成其餘的好比"shiroFilteraa",那麼當啓動tomcat時會發現報錯了,spring找不到name爲「shiroFilteraa的bean」.url

到目前爲止獲取到了targetBeanName的值爲shiroFilter,而shiroFilter它是一個Filter那麼就要調用它的init(FilterConfig config)方法初始化,這就是initFilterBean()這個方法中this.delegate = initDelegate(wac);的做用了。spa

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

相關文章
相關標籤/搜索