从源码和文档理解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中所有被注册为Filter的Bean,都被这个FilterChainProxy管理了,但不是直接管理的,是通过一组SecurityFilterChain来管理的
4. 选择合适的安全策略(SecurityFilterChain)
- 每个请求根据访问路径的不同,SpringSecurity会选择不同的安全策略
- 不同的安全策略则需要一套不同功能的
Filter来拦截处理 - 所以
FilterChainProxy会先查,哪个SecurityFilterChain能处理这个url - 看源码:
org.springframework.security.web.FilterChainProxy#getFilters(javax.servlet.http.HttpServletRequest) - 如果某个
SecurityFilterChain匹配到了这个请求,那么就用它保存的一组Filter来处理 - 通过这个
SecurityFilterChain来获取一组Filter,然后依次调用这些Filter - 这组
Filter由org.springframework.security.web.FilterChainProxy.VirtualFilterChain连接起来

那么SecurityFilterChain是如何管理安全策略的呢?
-
FilterChainProxy中有多个SecurityFilterChain,每一个SecurityFilterChain都代表了一种安全策略

-
SecurityFilterChain实例里面包含了一个RequestMatcher用来匹配某个Request,同时这个SecurityFilterChain实例也包含了一组Filter -
FilterChainProxy在拦截到一个Request的时候,会去看,哪个SecurityFilterChain能匹配上这个Request

-
当某个
SecurityFilterChain的实例匹配到了这个Request,FilterChainProxy会生成一个VirtualFilterChain,来把SecurityFilterChain中的Filters被串起来,依次执行 -
可以看看源码:
org.springframework.security.web.FilterChainProxy.VirtualFilterChain -
详见
org.springframework.security.web.FilterChainProxy.VirtualFilterChain#doFilter
5. 有用的Filter
6. Filter的顺序
SpringSecurity起到作用的Filter分三种,排成以下顺序
- 普通的各种SpringSecurity提供的
Filter,用来进行权限校验等行为 ExceptionTranslationFilter倒数第二道Filter,主要用来捕获处理权限异常FilterSecurityInterceptor最后一道Filter
- 他们之间是如何配合的呢?
-
- 各种
Filter会把权限校验的结果存在某个地方
- 各种
-
FilterSecurityInterceptor会检查这个权限校验的结果
-
- 如果权限校验通过,则直接调用Controller或者指定html页面
-
- 如果没有校验通过,则抛出权限相关的异常,把异常信息带出
-
ExceptionTranslationFilter捕获到权限相关的异常, 则转化为相对应的报错信息,返回给前端
//ExceptionTranslationFilter 伪代码:
try {
filterChain.doFilter(request, response); //直接由FilterSecurityInterceptor来处理,等待抛出异常
} catch (AccessDeniedException | AuthenticationException e) {
if (!authenticated || e instanceof AuthenticationException) {
startAuthentication(); //如果是非登录状态,则跳转登录逻辑,比如重定向至登录页
} else {
accessDenied(); //如果是权限不够,则进行某些处理,比如返回401
}
}
7.总结 一图流


本文深入探讨SpringSecurity的权限管理机制,介绍其如何利用Filter进行权限校验,使权限与业务逻辑解耦。通过DelegatingFilterProxy和FilterChainProxy,SpringSecurity在容器启动前注册Filter,动态管理权限策略。

982

被折叠的 条评论
为什么被折叠?



