Spring Security 官方文档学习(3)—— 在基于Servlet的应用程序中的高层体系结构

Spring Security 的 Servlet 支持是基于 Servlet 过滤器的。本文图片来自 Spring 官网

下图显示了单个HTTP请求的处理程序典型分层:

image.png

  • 客户端向应用程序发送请求,容器创建一个包含过滤器和 Servlet 的 FilterChain,这些过滤器和 Servlet 应该基于请求 URL 的路径来处理 HttpServletRequest。
  • 在 Spring MVC 应用程序中,Servlet 是 DispatcherServlet 的一个实例。
  • 一个 Servlet 最多可以处理一个 HttpServletRequest 与 HttpServletResponse。
  • 然而可以使用多个 Filter。
  • 每层过滤器可以修改下游过滤器和 Servlet 使用的 HttpServletRequest 或 HttpServletResponse

Filter 的强大功能来自传递给它的 FilterChain:

public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) {
    // do something before the rest of the application
    chain.doFilter(request, response); // invoke the rest of the application
    // do something after the rest of the application
}
复制代码

注意: 因为 Filter 只影响下游的 Filters 和 Servlet,所以调用每个 Filter 的顺序非常重要。


DelegatingFilterProxy

Spring 提供了一个名为 DelegatingFilterProxy 的过滤器实现,它允许在 Servlet 容器的生命周期和 Spring 的 ApplicationContext 之间进行桥接。Servlet 容器允许使用它自己的标准注册过滤器,但是它不知道 Spring 定义的 bean。DelegatingFilterProxy 可以通过标准的 Servlet 容器机制注册 Filter,同时可以将所有的工作委托给实现 Filter 的 Spring Bean。

image.png

DelegatingFilterProxy 的加入使得 Bean Filter0 的实例可以正常进行工作。

DelegatingFilterProxy 伪代码:

public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) {
    // Lazily get Filter that was registered as a Spring Bean
    // For the example in DelegatingFilterProxy delegate is an instance of Bean Filter0
    Filter delegate = getFilterBean(someBeanName);
    // delegate work to the Spring Bean
    delegate.doFilter(request, response);
}
复制代码

FilterChainProxy

Spring Security 的 Servlet 支持包含在 FilterChainProxy 中。FilterChainProxy 是 Spring Security 提供的一个特殊的过滤器,它允许通过 SecurityFilterChain 委托给许多 Filter 实例。由于FilterChainProxy 是一个 Bean,它通常被封装在 DelegatingFilterProxy 中。

image.png


SecurityFilterChain

SecurityFilterChain 被 FilterChainProxy 用来确定应该为这个请求调用哪个 Spring 安全过滤器。

image.png

注意:

SecurityFilterChain 中的安全过滤器通常是 bean,但是它们注册在 FilterChainProxy 而不是 DelegatingFilterProxy 上。FilterChainProxy 为直接注册 Servlet 容器或 DelegatingFilterProxy 提供了许多优势。首先,它为所有 Spring Security 的 Servlet 支持提供了一个起点。因此,如果在 Spring Security 的 Servlet 支持方面排除故障,那么在 FilterChainProxy 中添加一个调试点是一个很好的开始。

此外,它在确定何时应该调用 SecurityFilterChain 方面提供了更多的灵活性。在 Servlet 容器中,只根据 URL 调用过滤器。然而,FilterChainProxy 可以通过利用 RequestMatcher 接口来根据 HttpServletRequest 中的任何内容来确定调用。

image.png

上图所示,FilterChainProxy 决定应该使用哪个 SecurityFilterChain。只有第一个匹配的 SecurityFilterChain 将被调用。如果请求的 URL 是 /api/messages/,它将首先匹配 SecurityFilterChaing 的 /api/** 模式,因此只有 SecurityFilterChaing0 将被调用,即使它也匹配 SecurityFilterChainN。如果请求一个 /messages,它将不匹配 SecurityFilterChaing0 的 /api/** 模式,因此 FilterChainProxy 将继续尝试每个 SecurityFilterChain。假设没有其他 SecurityFilterChain 实例匹配,SecurityFilterChainN 将被调用。


Security Filters

Security Filter 通过 SecurityFilterChain API 插入到 FilterChainProxy 中。过滤器的顺序很重要。通常不需要知道 Spring Security 的 Filters 的顺序。但是,有时知道顺序是有益的:

顺序如下:

  • ChannelProcessingFilter
  • WebAsyncManagerIntegrationFilter
  • SecurityContextPersistenceFilter
  • HeaderWriterFilter
  • CorsFilter
  • CsrfFilter
  • LogoutFilter
  • OAuth2AuthorizationRequestRedirectFilter
  • Saml2WebSsoAuthenticationRequestFilter
  • X509AuthenticationFilter
  • AbstractPreAuthenticatedProcessingFilter
  • CasAuthenticationFilter
  • OAuth2LoginAuthenticationFilter
  • Saml2WebSsoAuthenticationFilter
  • UsernamePasswordAuthenticationFilter
  • OpenIDAuthenticationFilter
  • DefaultLoginPageGeneratingFilter
  • DefaultLogoutPageGeneratingFilter
  • ConcurrentSessionFilter
  • DigestAuthenticationFilter
  • BearerTokenAuthenticationFilter
  • BasicAuthenticationFilter
  • RequestCacheAwareFilter
  • SecurityContextHolderAwareRequestFilter
  • JaasApiIntegrationFilter
  • RememberMeAuthenticationFilter
  • AnonymousAuthenticationFilter
  • OAuth2AuthorizationCodeGrantFilter
  • SessionManagementFilter
  • ExceptionTranslationFilter
  • FilterSecurityInterceptor
  • SwitchUserFilter

处理安全异常

  • ExceptionTranslationFilter 允许将 AccessDeniedException 和 AuthenticationException 转换为 HTTP 响应。
  • ExceptionTranslationFilter 作为一个安全过滤器被插入到 FilterChainProxy 中。

image.png

  1. 首先,ExceptionTranslationFilter 通过调用 FilterChain.doFilter(request, response) 来唤醒应用程序的其余部分。

  2. 如果用户未经过身份验证或是 AuthenticationException,则启动认证:

    • 清除 SecurityContextHolder
    • HttpServletRequest 保存在 RequestCache 中。当用户成功通过身份验证时,将使用 RequestCache重放原始请求
    • AuthenticationEntryPoint 用于从客户端请求凭据。例如,它可能重定向到一个登录页面或发送一个 WWW-Authenticate 头。
  3. 否则,如果是 AccessDeniedException,则拒绝访问。调用 AccessDeniedHandler 来处理拒绝访问。

ExceptionTranslationFilter 伪代码如下:

try {
    filterChain.doFilter(request, response); 
} catch (AccessDeniedException | AuthenticationException ex) {
    if (!authenticated || ex instanceof AuthenticationException) {
        startAuthentication(); 
    } else {
        accessDenied(); 
    }
}
复制代码
© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享