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