【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

 

原文地址:https://www.cnblogs.com/cac2020/p/13821528.html