SpringSecurity学习1 SpringSecurity实现的基础Filter

参考: Servlet Security: The Big Picture

从源码和文档理解SpringSecurity

从宏观上来看
SpringSecurity是用了一系列Filter,来处理权限的问题
在请求,达到Servlet和Controller之前,就进行权限的校验
让权限和业务逻辑彻底解耦

以下是讲解,SpringSecurity是怎么用Filter的


1. 容器是如何使用Filter的

SpringSecurity是基于Servlet的Filter机制来实现的
所以先了解一下Filter是怎么工作的
下图表示了客户端一个请求,是怎么被Filter拦截的
在这里插入图片描述

  • 当客户端发送一个请求给应用的时候, 容器会创建一个 FilterChain 里面包含了多个Filters和一个处理这个请求的Servlet

  • 在SpringMVC中Servlet就是DispatcherServlet

  • 这种模型就是当一个请求HttpServletRequest过来的时候,对应着一个Servlet和多个Filter

  • 由于一个Filter只会影响下游的Filter和Servlet,所以Filter的顺序非常重要

//标准写法
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) {
    chain.doFilter(request, response); // invoke the rest of the application
    //例如Tomcat使用的ApplicationFilterChain
    //保存了所有的Filter,和要被访问的Servlet的实例
    //详见 org.apache.catalina.core.ApplicationFilterChain#internalDoFilter
}

2. SpringSecurity如何融入容器的拦截器Filter机制? (DelegatingFilterProxy)

  • DelegatingFilterProxy是一个容器直接使用的Filter,文中其他的Filter对容器来说是透明的,不可见的
  • 在容器的标准里,一个Filter想要注册到容器上,必须在容器启动之前就注册好,同时也意味着需要在Spring环境启动之前,就注册到容器上
  • 也就导致了一个问题,当我们在Spring环境中动态创造了一个实现了Filter接口的Bean,实际上是不能直接注册到容器的过滤器链FilterChain上的
  • 为了解决这个问题,Spring使用了代理模式,DelegatingFilterProxy就是一个代理,提前注册到了容器的FilterChain
  • 然后当Spring环境启动好之后,再用DelegatingFilterProxy来调用Spring初始化后生成的各种Bean
  • 在Spring环境中的Filter并没有直接加入到拦截器链上, 而是被一个DelegatingFilterProxy 代理了,相当于那么多的Filter在容器看来只被一个DelegatingFilterProxy处理了

下图表示了 DelegatingFilterProxy 在容器的Filter中的位置:
在这里插入图片描述


3. Spring管理的Filter是怎么执行doFilter的呢? (FilterChainProxy )

  • FilterChainProxy 是个特殊Filter,同时也是一个SpringBean,可以获取到所有的在Spring环境上注册的Bean
  • 实际上DelegatingFilterProxy就是包装了这个FilterChainProxy
  • 而关键就在于SpringSecurity中所有被注册为FilterBean,都被这个FilterChainProxy管理了,但不是直接管理的,是通过一组SecurityFilterChain来管理的

在这里插入图片描述

4. 选择合适的安全策略(SecurityFilterChain)

  • 每个请求根据访问路径的不同,SpringSecurity会选择不同的安全策略
  • 不同的安全策略则需要一套不同功能的Filter来拦截处理
  • 所以FilterChainProxy会先查,哪个SecurityFilterChain能处理这个url
  • 看源码:org.springframework.security.web.FilterChainProxy#getFilters(javax.servlet.http.HttpServletRequest)
  • 如果某个SecurityFilterChain匹配到了这个请求,那么就用它保存的一组Filter来处理
  • 通过这个SecurityFilterChain来获取一组Filter,然后依次调用这些Filter
  • 这组Filterorg.springframework.security.web.FilterChainProxy.VirtualFilterChain连接起来

在这里插入图片描述

那么SecurityFilterChain是如何管理安全策略的呢?
  • FilterChainProxy 中有多个SecurityFilterChain,每一个SecurityFilterChain都代表了一种安全策略
    在这里插入图片描述

  • SecurityFilterChain实例里面包含了一个RequestMatcher用来匹配某个Request,同时这个SecurityFilterChain实例也包含了一组Filter

  • FilterChainProxy在拦截到一个Request的时候,会去看,哪个SecurityFilterChain能匹配上这个Request
    FilterChainProxy

  • 当某个SecurityFilterChain的实例匹配到了这个Request, FilterChainProxy会生成一个VirtualFilterChain,来把SecurityFilterChain中的Filters被串起来,依次执行

  • 可以看看源码: org.springframework.security.web.FilterChainProxy.VirtualFilterChain

  • 详见org.springframework.security.web.FilterChainProxy.VirtualFilterChain#doFilter

5. 有用的Filter

Security Filters

6. Filter的顺序

SpringSecurity起到作用的Filter分三种,排成以下顺序

  1. 普通的各种SpringSecurity提供的Filter,用来进行权限校验等行为
  2. ExceptionTranslationFilter 倒数第二道Filter,主要用来捕获处理权限异常
  3. FilterSecurityInterceptor 最后一道Filter
  • 他们之间是如何配合的呢?
    1. 各种Filter会把权限校验的结果存在某个地方
    1. FilterSecurityInterceptor会检查这个权限校验的结果
    1. 如果权限校验通过,则直接调用Controller或者指定html页面
    1. 如果没有校验通过,则抛出权限相关的异常,把异常信息带出
    1. ExceptionTranslationFilter捕获到权限相关的异常, 则转化为相对应的报错信息,返回给前端
//ExceptionTranslationFilter 伪代码:
try {
    filterChain.doFilter(request, response);  //直接由FilterSecurityInterceptor来处理,等待抛出异常
} catch (AccessDeniedException | AuthenticationException e) {
    if (!authenticated || e instanceof AuthenticationException) {
        startAuthentication();  //如果是非登录状态,则跳转登录逻辑,比如重定向至登录页
    } else {
        accessDenied(); //如果是权限不够,则进行某些处理,比如返回401
    }
}

7.总结 一图流

在这里插入图片描述

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值