shiro学习笔记之filter

2,185 阅读4分钟

本篇文章聚焦 shiro 的 filter 相关逻辑,核心逻辑SecurityManagerSubjectAuthenticator后面再做详细分析。

在 web 应用中,shiro 发挥作用的切入点是根据 severlet 规范实现过滤器,通过过滤器对 web 请求进行拦截,然后做认证和权限检查。

那么 shiro 中都有哪些拦截器,在一个认证流程当中 filter 的执行顺序是怎么样的,是如何跟 web 系统集成的呢。

本文中的流程描述主要基于 OAuth2认证规范。

Filter 的注册

tomact 的 filter 信息是在ApplicationFilterChain中维护的,其结构是一个ApplicationFilterConfig数组,下面是其在源码中的定义:

    private ApplicationFilterConfig[] filters = new ApplicationFilterConfig[0];

想要知道shiro 的 filter 是如何注册进来的,可以在代码ApplicationFilterChain类中 filter 属性是如何往里面添加成员的。

FilterRegistrationBean对象分析

FilterRegistrationBean是在 spring 环境下,用户自定义 filter 注册的时候会用到的类。这个类的主要功能是包含了和 filter 相关的配置信息,包括filter,name,urlPatterns等,它之所以能在 tomact 启动的时候被加载到,是因为实现了ServletContextInitializer接口,注意这个接口不同于WebApplicationInitializer,并不会在 servlet 启动的时候被加载,而是受 spring 容器管理,根据 spring 的需要来调用对于的实现类。

Filter 注册到 Tomcat (embed)

在 spring 启动后会将配置为 bean 的 filter 注册到 spring 容器中。如下是 shiro filter 的注册到 spring 容器的方法。

@Configuration
public class FilterConfig {

    @Bean
    public FilterRegistrationBean shiroFilterRegistration() {
        FilterRegistrationBean registration = new FilterRegistrationBean();
        registration.setFilter(new DelegatingFilterProxy("shiroFilter"));
        //该值缺省为false,表示生命周期由SpringApplicationContext管理,设置为true则表示由ServletContainer管理
        registration.addInitParameter("targetFilterLifecycle", "true");
        registration.setEnabled(true);
        registration.setOrder(Integer.MAX_VALUE - 1);
        registration.addUrlPatterns("/*");
        return registration;
    }
}

其中 名为shiroFilter的 bean 是在 shiro factoryBean的配置中完成的,如下是 shiro FactoryBean 的配置:

@Bean("shiroFilter")
    public ShiroFilterFactoryBean shiroFilter(SecurityManager securityManager) {
        ShiroFilterFactoryBean shiroFilter = new ShiroFilterFactoryBean();
        shiroFilter.setSecurityManager(securityManager);
        //oauth过滤
        Map<String, Filter> filters = new HashMap<>();
        filters.put("oauth2", new OAuth2Filter());
        shiroFilter.setFilters(filters);

        Map<String, String> filterMap = new LinkedHashMap<>();
        filterMap.put("/webjars/**", "anon");
  			......
        filterMap.put("/**", "oauth2");
        shiroFilter.setFilterChainDefinitionMap(filterMap);

        return shiroFilter;
    }

ShiroFilterFactoryBean会将 shiro 的filter 相关配置保存生成一个名为shiroFilter的 bean,这个 bean 让后会被DelegatingFilterProxy代理,注册成为一个FilterRegistrationBean实例,最后这些 bean 在 tomact 启动的时候,在 onStartup方法中,一步步存放到 tomcat 的 filterMap 中,然后后续的 web 请求都会经过这些 filter。

Tomcat filter 执行流程

Shiro filter 关键对象介绍

先看一个类图。下图中展示了 shiro 中的主要过滤器,下面对这些 filter 的主要功能做介绍。

  • AdviceFilter:使用了类似 AOP 的编程思想,在执行 shiro 的 filterChain 之前和之后分别执行子类的preHandlepostHandle方法;
  • PathMatchingFilter:继承自AdviceFilter,做路径匹配的过滤器,最核心的是preHandle方法(AdviceFilter.preHandle会调用到这里),对于匹配上的请求,会调用isFilterChainContinued做下一步处理逻辑,最后会调用抽象方法onPreHandle
  • AccessControlFilter:继承自PathMatchingFilter,上一步调用到onPreHandle后,会执行isAccessAllowedonAccessDenied来判断请求是能容许通过,这两个方法都是抽象方法,具体实现逻辑由子类完成,用户也可以在自定义 filter 中覆写这两个方法来实现自己的需要的逻辑;
  • ``AuthenticationFilter`:这个接口功能比较简单,主要作用通过isAccessAllowed方法判断当前请求的用户是否已登录.
  • AuthenticatingFilter:最重要的功能是executeLogincreateToken ,其中executeLogin的动作最终调用Subject.login完成,这里是 shiro 的主要功能(SubjectSecurityManagerSession)类集中交互的地方。
  • OAuth2Filter:用户自定义 filter,继承自AuthenticatingFilter ,这里面 用户可以自定义createToken(是在 cookie 中存放 token 还是在 header 中直接存 token),onAccessDenied(未登录用户是直接返回401,还是进一步判断是否有 token 然后做下一步处理)等方法来完成自己的登录逻辑。
  • SpringShiroFilter:继承自AbstractShiroFilter,该类是ShiroFilterFactoryBeangetObject方法实例化,也就是会在 spring 初始化的时候实例化,是 将shiro 的核心功能(WebSecurityManager,FilterChainResolver)集成到 filter中的纽带。

Shiro Filter 执行序列图

shiro 的过滤器执行的流程主要分为三个阶段,

  • 第一阶段是 tomcat filter 通用流程;
  • 第二阶段是 Shiro 的 filter 流程,转化的纽带是DelegatingFilterProxySpringShiroFilter这两个跟 spring 相关的filter;
  • 第三阶段是用户自定义 filter 流程。主要根据用户实际需求晚上自定义逻辑。当然用户自定义 filter 只是覆写了几个方法,主要的流程还是在 shiro 的核心对象之间执行。