Spring-shiro源碼陶冶-DelegatingFilterProxy和ShiroFilterFactoryBean


閱讀源碼有助於陶冶情操,本文旨在簡單的分析shiro在Spring中的使用

介紹

Shiro是一個強大易用的Java安全框架,提供了認證、授權、加密和會話管理主要功能;另外其也提供了Web Support、緩存、Remember Me、並發等功能。
而Shiro的架構核心可看來自Java技術棧提供的圖片
shiro_structure

對上述的圖作簡單的描述

  • Subject 用戶視圖,shiro對用戶或者第三方服務、任何需要登錄的東西統一稱之為Subject對象
  • SecurityManager 安全管理器,其中內含緩存管理、會話管理,主要是對登錄過來的Subject視圖作一系列的安全操作(包括下述提及的Realms的關聯使用)
  • Realms 數據管理器,其主要充當shiro與安全數據交互的橋梁,幫助shiro容器完成用戶的校驗以及認證功能。可配置多個,但必須配置至少一個。shiro也提供了默認的實現比如ladp/jdbc等方式的用戶校驗認證

更多的部分讀者可參閱知乎Java技術棧專欄文章非常詳盡的 Shiro 架構解析。本文只分析其與Spring如何搭配使用

web.xml配置Shiro環境

配置清單如下

 <!-- shiro 安全過濾器 -->
    <filter>
        <filter-name>shiroFilter</filter-name>
		<!--spring調用shiro代理-->
        <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
        <async-supported>true</async-supported>
        <init-param>
			<!--是否開啟Filter的生命周期,主要涉及init和destory-->
            <param-name>targetFilterLifecycle</param-name>
            <param-value>true</param-value>
        </init-param>
    </filter>

    <filter-mapping>
        <filter-name>shiroFilter</filter-name>
        <url-pattern>/*</url-pattern>
        <dispatcher>REQUEST</dispatcher>
    </filter-mapping>

DelegatingFilterProxy#initFilterBean

入口方法,首先會執行初始化工作,shiro和spring security的代理加載實體Filter類都是通過此入口方法,代碼清單如下

	@Override
	protected void initFilterBean() throws ServletException {
		synchronized (this.delegateMonitor) {
			if (this.delegate == null) {
				// If no target bean name specified, use filter name.如果沒有指定則采用對應的<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);
				}
			}
		}
	}

我們主要關心DelegatingFilterProxy#initDelegate初始化委托Filter方法,代碼清單如下

	protected Filter initDelegate(WebApplicationContext wac) throws ServletException {
		//根據名字獲取ApplicationContext環境的bean,且必須是Filter的實現類
		Filter delegate = wac.getBean(getTargetBeanName(), Filter.class);
		//true則初始化對應的Filter類
		if (isTargetFilterLifecycle()) {
			//這里一般對應的Filter類為`org.apache.shiro.spring.web.ShiroFilterFactoryBean`
			delegate.init(getFilterConfig());
		}
		return delegate;
	}

由以上代碼可以確認,application-shiro.xmlSpring配置文件必須含有與web.xmlDelegatingFilterProxy類對應的<filter-name>的bean配置

示例文件

	<!-- Shiro的Web過濾器 -->
    <bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
		<!--securityManager屬性必須存在,通過安全管理器調用Realm接口實現的認證/授權方法-->
        <property name="securityManager" ref="securityManager"/>
        <!--登錄、主頁、未通過授權頁面的url-->
        <property name="loginUrl" value="/login"/>
        <property name="successUrl" value="/index.html"/>
        <property name="unauthorizedUrl" value="/403.html"/>
        <!--自定義的filter集合-->
       <property name="filters">
            <!--這里采用map集合主要是響應內部屬性Map<String, Filter> filters-->
            <util:map>
                <entry key="authc" value-ref="formAuthenticationFilter"/>
                <entry key="perm" value-ref="permissionsAuthorizationFilter"/>
                <entry key="sysUser" value-ref="sysUserFilter"/>
                <entry key="user" value-ref="userFilter"/>
            </util:map>
        </property>
	<!--對應路徑請求Filter鏈,=右邊代表是filter過濾引用,且是順序執行,url且從上置下優先匹配,一旦匹配則不往下搜尋-->
	<!--=右邊表達也可為authc[role1,role2]表明訪問該url必須通過認證且具有role1、role2的角色權限-->
        <property name="filterChainDefinitions">
            <value>
                /test/** = anon
                /login = captcha,authc
                /index = anon
                /403.html = anon
                /login.html = anon
                /favicon.ico = anon
                /static/** = anon
                /index.html=user,sysUser
                /welcome.html=user,sysUser
                /** = user,sysUser,perm
            </value>
        </property>
    </bean>

ShiroFilterFactoryBean#createInstance方法返回Filter實例

通過ShiroFilterFactoryBean#createInstance方法創建對應的Filter實例,我們看下創建的實例是何種人物,代碼清單如下

	//創建實例,實例對象為SpringShiroFilter
	protected AbstractShiroFilter createInstance() throws Exception {

        log.debug("Creating Shiro Filter instance.");
		//<property name="securityManager">屬性必須存在
        SecurityManager securityManager = getSecurityManager();
        if (securityManager == null) {
            String msg = "SecurityManager property must be set.";
            throw new BeanInitializationException(msg);
        }
		//securityManager的實現類必須是WebSecurityManager的實現類,表明Spring的shiro結合是web方面的
        if (!(securityManager instanceof WebSecurityManager)) {
            String msg = "The security manager does not implement the WebSecurityManager interface.";
            throw new BeanInitializationException(msg);
        }
		//filter過濾鏈管理類創建
        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);
    }

其中涉及filter過濾鏈管理類的創建,簡單分析下,代碼清單如下

	protected FilterChainManager createFilterChainManager() {
		//采用默認filter過濾鏈管理類
        DefaultFilterChainManager manager = new DefaultFilterChainManager();
		//獲取默認Filter類集合
        Map<String, Filter> defaultFilters = manager.getFilters();
        //apply global settings if necessary:
        for (Filter filter : defaultFilters.values()) {
	   //主要設置loginUrl、successUrl、unauthorizedUrl
	   //即權限Filter類設置loginUrl;認證Filter類設置successUrl、loginUrl;授權Filter類設置unauthorizedUrl,loginUrl
            applyGlobalPropertiesIfNecessary(filter);
        }

        //Apply the acquired and/or configured filters:用戶自定義的Filter類,通過<property name="filters">設定的
        Map<String, Filter> filters = getFilters();
        if (!CollectionUtils.isEmpty(filters)) {
            for (Map.Entry<String, Filter> entry : filters.entrySet()) {
                String name = entry.getKey();
                Filter filter = entry.getValue();
				//同樣設置url屬性
                applyGlobalPropertiesIfNecessary(filter);
				//設置名字
                if (filter instanceof Nameable) {
                    ((Nameable) filter).setName(name);
                }
                //'init' argument is false, since Spring-configured filters should be initialized
                //in Spring (i.e. 'init-method=blah') or implement InitializingBean:
                manager.addFilter(name, filter, false);
            }
        }

        //build up the chains: url與對應的filter過濾鏈配置,解析的是<property name="filterChainDefinitions">屬性
		//url可對應多個filter,且保存filter是通過ArrayList來保存的,表明過濾鏈則由寫的先后順序執行
        Map<String, String> chains = getFilterChainDefinitionMap();
        if (!CollectionUtils.isEmpty(chains)) {
            for (Map.Entry<String, String> entry : chains.entrySet()) {
                String url = entry.getKey();
				//chainDefinition可能為authc[role1,role2]或者authc,anno等
                String chainDefinition = entry.getValue();
				//解析以上表達式並保存至相應的自定義filter對象中
                manager.createChain(url, chainDefinition);
            }
        }

        return manager;
    }

總結

  1. Spring容器中使用Apache Shiro,需要配置

    • web.xml中配置 節點,節點類為org.springframework.web.filter.DelegatingFilterProxy
    • spring配置shiro文件中需要 節點,節點id名必須與web.xml定義中的 節點的 屬性一致,且beanClass為 org.apache.shiro.spring.web.ShiroFilterFactoryBean
  2. Spring配置文件中配置org.apache.shiro.spring.web.ShiroFilterFactoryBean主要設置shiro的相關filter用於攔截請求,其中的屬性含義見本文的示例文件說明

下節預告

Spring-shiro源碼陶冶-DefaultFilter


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM