【Shiro學習之四】shiro攔截器


apahce shiro:1.6.0,依賴shiro-web部分

一、shiro與web集成
1、Shiro1.1 及以前版本配置方式
使用org.apache.shiro.web.servlet.IniShiroFilter作為Shiro安全控制的入口點。

web.xml:

<?xml version="1.0" encoding="UTF-8"?>
<web-app
        xmlns="http://java.sun.com/xml/ns/javaee"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
        version="3.0"
        metadata-complete="false">
    <!--- shiro 1.1 -->
    <filter>
        <filter-name>iniShiroFilter</filter-name>
        <filter-class>org.apache.shiro.web.servlet.IniShiroFilter</filter-class>
        <init-param>
            <param-name>configPath</param-name>
            <param-value>classpath:shiro.ini</param-value> <!--默認先從/WEB-INF/shiro.ini,如果沒有找classpath:shiro.ini-->
        </init-param>
        <init-param>
            <param-name>config</param-name>
            <param-value>
                [main]
                authc.loginUrl=/login

                [users]
                zhang=123,admin

                [roles]
                admin=user:*,menu:*

                [urls]
                /login=anon
                /static/**=anon
                /authenticated=authc
                /role=authc,roles[admin]
                /permission=authc,perms["user:create"]
            </param-value>
        </init-param>
    </filter>
    <filter-mapping>
        <filter-name>iniShiroFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>
    <error-page>
        <error-code>401</error-code>
        <location>/WEB-INF/jsp/unauthorized.jsp</location>
    </error-page>

</web-app>
View Code

2、Shiro 1.2 及以后版本的配置方式 

使用org.apache.shiro.web.env.EnvironmentLoaderListener來創建相應的WebEnvironment,並自動綁定到ServletContext,默認使用org.apache.shiro.web.env.IniWebEnvironment實現。

web.xml:

 

<?xml version="1.0" encoding="UTF-8"?>
<web-app
        xmlns="http://java.sun.com/xml/ns/javaee"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
        version="3.0"
        metadata-complete="false">
    <!--- shiro 1.2 -->
    <listener>
        <listener-class>org.apache.shiro.web.env.EnvironmentLoaderListener</listener-class>
    </listener>
    <context-param>
        <param-name>shiroEnvironmentClass</param-name>
        <param-value>org.apache.shiro.web.env.IniWebEnvironment</param-value><!-- 默認先從/WEB-INF/shiro.ini,如果沒有找classpath:shiro.ini -->
    </context-param>
    <context-param>
        <param-name>shiroConfigLocations</param-name>
        <param-value>classpath:shiro.ini</param-value>
    </context-param>
    <filter>
        <filter-name>shiroFilter</filter-name>
        <filter-class>org.apache.shiro.web.servlet.ShiroFilter</filter-class>
    </filter>
    <filter-mapping>
        <filter-name>shiroFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>

</web-app>
View Code

3、與Spring集成 

使用org.springframework.web.filter.DelegatingFilterProxy作為入口,DelegatingFilterProxy自動到spring容器查找名字為 shiroFilter的bean並把所有 Filter 的操作委托給它。然后將ShiroFilter配置到spring容器即可
目前基本上都是用於spring集成的方式。

web.xml:

<?xml version="1.0" encoding="UTF-8"?>
<web-app
        xmlns="http://java.sun.com/xml/ns/javaee"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
        version="3.0"
        metadata-complete="false">

    <!-- Spring配置文件開始  -->
    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>
            classpath:spring-beans.xml,
            classpath:spring-shiro-web.xml
        </param-value>
    </context-param>
    <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>
    <!-- Spring配置文件結束 -->

    <!-- shiro 安全過濾器 -->
    <!-- The filter-name matches name of a 'shiroFilter' bean inside applicationContext.xml -->
    <filter>
        <filter-name>shiroFilter</filter-name>
        <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
        <async-supported>true</async-supported>
        <init-param>
            <param-name>targetFilterLifecycle</param-name>
            <param-value>true</param-value>
        </init-param>
    </filter>

    <!-- Make sure any request you want accessible to Shiro is filtered. /* catches all -->
    <!-- requests.  Usually this filter mapping is defined first (before all others) to -->
    <!-- ensure that Shiro works in subsequent filters in the filter chain:             -->
    <filter-mapping>
        <filter-name>shiroFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>

    <servlet>
        <servlet-name>spring</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>classpath:spring-mvc.xml</param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
        <async-supported>true</async-supported>
    </servlet>
    <servlet-mapping>
        <servlet-name>spring</servlet-name>
        <url-pattern>/</url-pattern>
    </servlet-mapping>


</web-app>
View Code

spring-shiro-web.xml:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:util="http://www.springframework.org/schema/util"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="
       http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd">

    <!-- 緩存管理器 使用Ehcache實現 -->
    <bean id="cacheManager" class="org.apache.shiro.cache.ehcache.EhCacheManager">
        <property name="cacheManagerConfigFile" value="classpath:ehcache.xml"/>
    </bean>

    <!-- 憑證匹配器 -->
    <bean id="credentialsMatcher" class="com.github.zhangkaitao.shiro.chapter12.credentials.RetryLimitHashedCredentialsMatcher">
        <constructor-arg ref="cacheManager"/>
        <property name="hashAlgorithmName" value="md5"/>
        <property name="hashIterations" value="2"/>
        <property name="storedCredentialsHexEncoded" value="true"/>
    </bean>

    <!-- Realm實現 -->
    <bean id="userRealm" class="com.github.zhangkaitao.shiro.chapter12.realm.UserRealm">
        <property name="userService" ref="userService"/>
        <property name="credentialsMatcher" ref="credentialsMatcher"/>
        <property name="cachingEnabled" value="true"/>
        <property name="authenticationCachingEnabled" value="true"/>
        <property name="authenticationCacheName" value="authenticationCache"/>
        <property name="authorizationCachingEnabled" value="true"/>
        <property name="authorizationCacheName" value="authorizationCache"/>
    </bean>

    <!-- 會話ID生成器 -->
    <bean id="sessionIdGenerator" class="org.apache.shiro.session.mgt.eis.JavaUuidSessionIdGenerator"/>

    <!-- 會話Cookie模板 -->
    <bean id="sessionIdCookie" class="org.apache.shiro.web.servlet.SimpleCookie">
        <constructor-arg value="sid"/>
        <property name="httpOnly" value="true"/>
        <property name="maxAge" value="180000"/>
    </bean>

    <!-- 會話DAO -->
    <bean id="sessionDAO" class="org.apache.shiro.session.mgt.eis.EnterpriseCacheSessionDAO">
        <property name="activeSessionsCacheName" value="shiro-activeSessionCache"/>
        <property name="sessionIdGenerator" ref="sessionIdGenerator"/>
    </bean>

    <!-- 會話驗證調度器 -->
    <bean id="sessionValidationScheduler" class="org.apache.shiro.session.mgt.quartz.QuartzSessionValidationScheduler">
        <property name="sessionValidationInterval" value="1800000"/>
        <property name="sessionManager" ref="sessionManager"/>
    </bean>

    <!-- 會話管理器 -->
    <bean id="sessionManager" class="org.apache.shiro.web.session.mgt.DefaultWebSessionManager">
        <property name="globalSessionTimeout" value="1800000"/>
        <property name="deleteInvalidSessions" value="true"/>
        <property name="sessionValidationSchedulerEnabled" value="true"/>
        <property name="sessionValidationScheduler" ref="sessionValidationScheduler"/>
        <property name="sessionDAO" ref="sessionDAO"/>
        <property name="sessionIdCookieEnabled" value="true"/>
        <property name="sessionIdCookie" ref="sessionIdCookie"/>
    </bean>

    <!-- 安全管理器 -->
    <bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
        <property name="realm" ref="userRealm"/>
        <property name="sessionManager" ref="sessionManager"/>
        <property name="cacheManager" ref="cacheManager"/>
    </bean>

    <!-- 相當於調用SecurityUtils.setSecurityManager(securityManager) -->
    <bean class="org.springframework.beans.factory.config.MethodInvokingFactoryBean">
        <property name="staticMethod" value="org.apache.shiro.SecurityUtils.setSecurityManager"/>
        <property name="arguments" ref="securityManager"/>
    </bean>

    <!-- 基於Form表單的身份驗證過濾器 -->
    <bean id="formAuthenticationFilter" class="org.apache.shiro.web.filter.authc.FormAuthenticationFilter">
        <property name="usernameParam" value="username"/>
        <property name="passwordParam" value="password"/>
        <property name="loginUrl" value="/login.jsp"/>
    </bean>

    <!-- Shiro的Web過濾器 -->
    <bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
        <property name="securityManager" ref="securityManager"/>
        <property name="loginUrl" value="/login.jsp"/>
        <property name="unauthorizedUrl" value="/unauthorized.jsp"/>
        <property name="filters">
            <util:map>
                <entry key="authc" value-ref="formAuthenticationFilter"/>
            </util:map>
        </property>
        <property name="filterChainDefinitions">
            <value>
                /index.jsp = anon
                /unauthorized.jsp = anon
                /login.jsp = authc
                /logout = logout
                /** = user
            </value>
        </property>
    </bean>

    <!-- Shiro生命周期處理器-->
    <bean id="lifecycleBeanPostProcessor" class="org.apache.shiro.spring.LifecycleBeanPostProcessor"/>

</beans>
View Code

二、常用shiro內置攔截器

1、NameableFilter
NameableFilter給Filter起個名字,如果沒有設置默認就是FilterName;當我們組裝攔截器鏈時會根據這個名字找到相應的攔截器實例;
2、OncePerRequestFilter
OncePerRequestFilter用於防止多次執行Filter;也就是說一次請求只會走一次攔截器鏈;另外提供enabled屬性,表示是否開啟該攔截器實例,默認enabled=true表示開啟,如果不想讓某個攔截器工作,可以設置為false即可。
3、ShiroFilter
ShiroFilter是整個Shiro的入口點,用於攔截需要安全控制的請求進行處理。
4、AdviceFilter
AdviceFilter提供了AOP風格的支持,類似於SpringMVC中的Interceptor.
5、PathMatchingFilter
PathMatchingFilter 提供了基於Ant風格的請求路徑匹配功能及攔截器參數解析的功能,如"roles[admin,user]"自動根據","分割解析到一個路徑參數配置並綁定到相應的路徑:
6、AccessControlFilter
AccessControlFilter提供了訪問控制的基礎功能;比如是否允許訪問/當訪問拒絕時如何處理等
7、默認攔截器
org.apache.shiro.web.filter.mgt.DefaultFilter 中的枚舉攔截器

public enum DefaultFilter {
    anon(AnonymousFilter.class),
    authc(FormAuthenticationFilter.class),
    authcBasic(BasicHttpAuthenticationFilter.class),
    authcBearer(BearerHttpAuthenticationFilter.class),
    logout(LogoutFilter.class),
    noSessionCreation(NoSessionCreationFilter.class),
    perms(PermissionsAuthorizationFilter.class),
    port(PortFilter.class),
    rest(HttpMethodPermissionFilter.class),
    roles(RolesAuthorizationFilter.class),
    ssl(SslFilter.class),
    user(UserFilter.class),
    invalidRequest(InvalidRequestFilter.class);
}

8、自定義攔截器
通過自定義自己的攔截器可以擴展一些功能,諸如動態 url-角色/權限訪問控制的實現、根據 Subject 身份信息獲取用戶信息綁定到 Request(即設置通用數據)、驗證碼驗證、在線用戶信息的保存等等,因為其本質就是一個 Filter;所以 Filter 能做的它就能做.

(1)默認攔截器接口定義有三個方法

public interface Filter {
    //初始化
    void init(FilterConfig var1) throws ServletException;
    //執行攔截邏輯
    void doFilter(ServletRequest var1, ServletResponse var2, FilterChain var3) throws IOException, ServletException;
    //攔截器銷毀回調方法
    void destroy();
}

(2)根據需要繼承相應的父類 重寫相應方法即可
如果我們想進行訪問控制就可以繼承AccessControlFilter;
如果我們要添加一些通用數據我們可以直接繼承PathMatchingFilter;

三、攔截器鏈
Shiro對Servlet容器的FilterChain進行了代理,即ShiroFilter在繼續 Servlet 容器的 Filter鏈的執行之前,通過 ProxiedFilterChain 對 Servlet 容器的 FilterChain 進行了代理;即先走
Shiro 自己的 Filter 體系,然后才會委托給 Servlet 容器的 FilterChain 進行 Servlet 容器級別的 Filter 鏈執行; Shiro 的 ProxiedFilterChain 執行流程: 1、 先執行 Shiro 自己的 Filter 鏈; 2、再執行 Servlet 容器的 Filter 鏈(即原始的 Filter)。

1、攔截器鏈管理器
接口FilterChainManager,用於注冊 Filter;注冊 URL-Filter 的映射關系。

public interface FilterChainManager {
    //返回注冊當當前FilterChainManager過濾器
    Map<String, Filter> getFilters();

    //根據過濾器鏈名稱返回過濾器鏈
    NamedFilterList getChain(String chainName);

    //校驗是否有可用的過濾器鏈
    boolean hasChains();

    //返回所有過濾器鏈的名稱
    Set<String> getChainNames();

    //輸入一個源過濾器鏈返回一個名字為chainName的代理過濾器鏈
    //交給SimpleNamedFilterList代理最終包裝成一個ProxiedFilterChain返回
    FilterChain proxy(FilterChain original, String chainName);

    //注冊過濾器
    void addFilter(String name, Filter filter);

    //注冊過濾器 添加之前是否要初始化
    void addFilter(String name, Filter filter, boolean init);

    //根據給定的名字和格式字符串定義創建過濾器鏈
    void createChain(String chainName, String chainDefinition);

    //對於沒有匹配到的請求路徑 通常是/** 創建一個默認過濾器鏈
    void createDefaultChain(String chainName);

    //注冊 URL-Filter 的映射關系
    void addToChain(String chainName, String filterName);
    void addToChain(String chainName, String filterName, String chainSpecificFilterConfig) throws ConfigurationException;

    //配置一個全局的過濾器集合用於所有請求路徑 這個集合將先於創建和設置過濾器鏈之前匹配
    void setGlobalFilters(List<String> globalFilterNames) throws ConfigurationException;
}
View Code

實現類DefaultFilterChainManager在構造器中會默認添加org.apache.shiro.web.filter.mgt.DefaultFilter中聲明的攔截器。

(1)shiro1.2注冊攔截器的流程?
應用啟動時:

監聽器org.apache.shiro.web.env.EnvironmentLoaderListener
-->org.apache.shiro.web.env.EnvironmentLoader
-->org.apache.shiro.web.env.IniWebEnvironment(默認是IniWebEnvironment,可以配置成自定義的類)
-->默認加載classpath:shiro.ini文件進行解析
-->創建web安全管理器WebSecurityManager
-->IniFilterChainResolverFactory::createInstance通過IniFilterChainResolverFactory工廠創建攔截器鏈處理器,默認是PathMatchingFilterChainResolver
-->PathMatchingFilterChainResolver::getFilterChainManager獲取攔截器鏈管理器DefaultFilterChainManager
-->IniFilterChainResolverFactory::buildChains創建過濾器鏈
-->IniFilterChainResolverFactory::registerFilters-->DefaultFilterChainManager::addFilter注冊過濾器存放到LinkedHashMap<String, Filter>結構 -->IniFilterChainResolverFactory::createChains-->DefaultFilterChainManager::createChain創建過濾器鏈 url部分是chainname存放到LinkedHashMap<String, NamedFilterList>結構

 

如果要自定義FilterChainResolver,可以自定義MyIniWebEnvironment.java繼承IniWebEnvironment來重寫上面邏輯
MyIniWebEnvironment.java:

package com.github.zhangkaitao.shiro.chapter8.web.env;

import org.apache.shiro.util.ClassUtils;
import org.apache.shiro.web.env.IniWebEnvironment;
import org.apache.shiro.web.filter.authc.FormAuthenticationFilter;
import org.apache.shiro.web.filter.authz.RolesAuthorizationFilter;
import org.apache.shiro.web.filter.mgt.DefaultFilter;
import org.apache.shiro.web.filter.mgt.DefaultFilterChainManager;
import org.apache.shiro.web.filter.mgt.FilterChainResolver;
import org.apache.shiro.web.filter.mgt.PathMatchingFilterChainResolver;

import javax.servlet.Filter;

public class MyIniWebEnvironment extends IniWebEnvironment {
    @Override
    protected FilterChainResolver createFilterChainResolver() {
        //在此處擴展自己的FilterChainResolver
        //1、創建FilterChainResolver
        PathMatchingFilterChainResolver filterChainResolver =
                new PathMatchingFilterChainResolver();
        //2、創建FilterChainManager
        DefaultFilterChainManager filterChainManager = new DefaultFilterChainManager();
        //3、注冊Filter
        for(DefaultFilter filter : DefaultFilter.values()) {
            filterChainManager.addFilter(filter.name(), (Filter) ClassUtils.newInstance(filter.getFilterClass()));
        }
        //4、注冊URL-Filter的映射關系
        filterChainManager.addToChain("/login.jsp", "authc");
        filterChainManager.addToChain("/unauthorized.jsp", "anon");
        filterChainManager.addToChain("/**", "authc");
        filterChainManager.addToChain("/**", "roles", "admin");

        //5、設置Filter的屬性
        FormAuthenticationFilter authcFilter =
                (FormAuthenticationFilter)filterChainManager.getFilter("authc");
        authcFilter.setLoginUrl("/login.jsp");
        RolesAuthorizationFilter rolesFilter = (RolesAuthorizationFilter)filterChainManager.getFilter("roles");
        rolesFilter.setUnauthorizedUrl("/unauthorized.jsp");

        filterChainResolver.setFilterChainManager(filterChainManager);
        return filterChainResolver;
    }
}
View Code

web.xml:

<?xml version="1.0" encoding="UTF-8"?>
<web-app
        xmlns="http://java.sun.com/xml/ns/javaee"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
        version="3.0"
        metadata-complete="false">
    <!--- shiro 1.2 -->
    <listener>
        <listener-class>org.apache.shiro.web.env.EnvironmentLoaderListener</listener-class>
    </listener>
    <context-param>
        <param-name>shiroEnvironmentClass</param-name>
        <param-value>com.github.zhangkaitao.shiro.chapter8.web.env.MyIniWebEnvironment</param-value>
    </context-param>
    <context-param>
        <param-name>shiroConfigLocations</param-name>
        <param-value>classpath:shiro.ini</param-value>
    </context-param>
    <filter>
        <filter-name>shiroFilter</filter-name>
        <filter-class>org.apache.shiro.web.servlet.ShiroFilter</filter-class>
    </filter>
    <filter-mapping>
        <filter-name>shiroFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>
</web-app>
View Code

(2)shiro與spring整合注冊攔截器的流程?

 結合一個整合Spring的web.xml配置來回顧一下web.xml各組件的啟動順序以及訪問時的執行順序:

<?xml version="1.0" encoding="UTF-8"?>
<web-app
        xmlns="http://java.sun.com/xml/ns/javaee"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
        version="3.0"
        metadata-complete="false">

    <!-- Spring配置文件開始  -->
    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>
            classpath:spring-beans.xml,
            classpath:spring-shiro-web.xml
        </param-value>
    </context-param>
    <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>
    <!-- Spring配置文件結束 -->

    <!-- shiro 安全過濾器 -->
    <!-- The filter-name matches name of a 'shiroFilter' bean inside applicationContext.xml -->
    <filter>
        <filter-name>shiroFilter</filter-name>
        <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
        <async-supported>true</async-supported>
        <init-param>
            <param-name>targetFilterLifecycle</param-name>
            <param-value>true</param-value>
        </init-param>
    </filter>

    <!-- Make sure any request you want accessible to Shiro is filtered. /* catches all -->
    <!-- requests.  Usually this filter mapping is defined first (before all others) to -->
    <!-- ensure that Shiro works in subsequent filters in the filter chain:             -->
    <filter-mapping>
        <filter-name>shiroFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>

    <servlet>
        <servlet-name>spring</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>classpath:spring-mvc.xml</param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
        <async-supported>true</async-supported>
    </servlet>
    <servlet-mapping>
        <servlet-name>spring</servlet-name>
        <url-pattern>/</url-pattern>
    </servlet-mapping>


</web-app>
View Code

啟動順序:先執行監聽器public void contextInitialized(ServletContextEvent event)方法,然后執行攔截器Filter的public void init()方法

用戶訪問執行順序:先執行攔截器filter的doFilter方法,在執行Servlet的doGet/doPost/service方法;

流程:

Spring監聽器org.springframework.web.context.ContextLoaderListener::contextInitialized
-->org.springframework.web.context.ContextLoader::initWebApplicationContext加載spring-beans.xml、spring-shiro-web.xml注冊bean到Spring容器上下文
-->spring-shiro-web.xml初始化和shiro相關的各個類,這里看一下Shiro的Web過濾器shiroFilter,實現類是org.apache.shiro.spring.web.ShiroFilterFactoryBean
-->org.apache.shiro.spring.web.ShiroFilterFactoryBean::createInstance創建SecurityManager、FilterChainManager、PathMatchingFilterChainResolver,返回SpringShiroFilter實例,和原生ShiroFilter一樣要繼承AbstractShiroFilter -->org.apache.shiro.spring.web.ShiroFilterFactoryBean::createFilterChainManager在創建過濾器鏈管理器時將過濾器、過濾器鏈注冊到過濾器鏈管理器

攔截器:org.springframework.web.filter.DelegatingFilterProxy::init沒有重寫init 則執行父類的init方法
-->org.springframework.web.filter.GenericFilterBean::init
-->org.springframework.web.filter.DelegatingFilterProxy::initFilterBean這一步是獲取web.xml中配置的<filter-name>內容 也就是shiroFilter 賦值給targetBeanName
-->org.springframework.web.filter.DelegatingFilterProxy::initDelegate這一步很重要就是根據targetBeanName將之前注冊好的ShiroFilter賦值給DelegatingFilterProxy里的delegate

 

2、攔截器鏈處理器

接口FilterChainResolver:解析請求url,然后將URL和攔截器鏈管理器存放的攔截器鏈模式匹配,根據傳入原始的chain得到一個代理的chain,默認包裝成ProxiedFilterChain

public interface FilterChainResolver {
    FilterChain getChain(ServletRequest request, ServletResponse response, FilterChain originalChain);
}

默認實現類:PathMatchingFilterChainResolver:按照Ant風格路徑匹配的處理器

(1)shiro1.2執行攔截器鏈的流程?

web.xml配置的入口filter是org.apache.shiro.web.servlet.ShiroFilter,會執行doFilter方法,ShiroFilter本身沒有實現doFilter方法,則向上執行父類的doFilter方法:
請求url-->org.apache.shiro.web.servlet.ShiroFilter::doFilter方法
-->org.apache.shiro.web.servlet.OncePerRequestFilter::doFilter方法
-->org.apache.shiro.web.servlet.AbstractShiroFilter::doFilterInternal組裝Subject
-->org.apache.shiro.subject.subject::execute
-->org.apache.shiro.web.servlet.AbstractShiroFilter::executeChain
-->org.apache.shiro.web.servlet.AbstractShiroFilter::getExecutionChain
-->PathMatchingFilterChainResolver::getChain在這里根據請求地址獲取對應代理攔截器鏈
-->ProxiedFilterChain::doFilter依次執行攔截器鏈上的攔截器

(2)shiro與Spring整合執行攔截器鏈的流程?

 

攔截器:org.springframework.web.filter.DelegatingFilterProxy::doFilter
-->org.springframework.web.filter.DelegatingFilterProxy::invokeDelegate 這里delegate就是注入的ShiroFilter
-->org.apache.shiro.spring.web.ShiroFilterFactoryBean.SpringShiroFilter::doFilter沒有重寫 調用父類doFilter方法
-->org.apache.shiro.web.servlet.OncePerRequestFilter::doFilter方法
-->org.apache.shiro.web.servlet.AbstractShiroFilter::doFilterInternal組裝Subject
-->org.apache.shiro.subject.subject::execute
-->org.apache.shiro.web.servlet.AbstractShiroFilter::executeChain
-->org.apache.shiro.web.servlet.AbstractShiroFilter::getExecutionChain
-->PathMatchingFilterChainResolver::getChain在這里根據請求地址獲取對應代理攔截器鏈
-->ProxiedFilterChain::doFilter依次執行攔截器鏈上的攔截器

 

3、攔截器鏈
javax.servlet.FilterChain:攔截器鏈接口,里面就一個方法執行攔截器

public interface FilterChain {
    void doFilter(ServletRequest var1, ServletResponse var2) throws IOException, ServletException;
}

shiro里的實現:ProxiedFilterChain.java

public class ProxiedFilterChain implements FilterChain {
    private static final Logger log = LoggerFactory.getLogger(ProxiedFilterChain.class);
    private FilterChain orig;
    private List<Filter> filters;
    private int index = 0;
    
    //構造攔截器鏈
    public ProxiedFilterChain(FilterChain orig, List<Filter> filters) {
        if (orig == null) {
            throw new NullPointerException("original FilterChain cannot be null.");
        }
        this.orig = orig;
        this.filters = filters;
        this.index = 0;
    }
    //依次執行list中攔截器
    public void doFilter(ServletRequest request, ServletResponse response) throws IOException, ServletException {
        if (this.filters == null || this.filters.size() == this.index) {
            //當執行完攔截器鏈中所有攔截器后 然后執行元攔截器鏈中攔截器
            this.orig.doFilter(request, response);
        } else {
            //順序執行攔截器鏈中的攔截器
            this.filters.get(this.index++).doFilter(request, response, this);
        }
    }
}

 

代碼及內容參考:張開濤-跟我學shiro

 


免責聲明!

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



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