Spring Security 架构

2,126 阅读4分钟

本文主要讨论下 Spring Web 应用相关的 Spring Security 架构。

1.Servlet Filter 回顾

Spring Security 是基于 Servlet Filter 的。所以先大致了解一下 Filter 的作用是有帮助的。下图显示了处理单个 HTTP 请求的程序的典型分层。

image.png

客户端向服务发送请求,Web 容器创建一个 FilterChain 实例,该实例包含一组 Filter,以及一个Servlet。Servlet 根据请求路径处理来处理 HttpServletRequest。在 Spring MVC 应用中,Servlet 是 DispatcherServlet. 最多只有一个 Servlet可以处理单个 HttpServletRequest 和 HttpServletResponse。但是可以有多个Filter来执行以下逻辑:

  • 阻止后续的 Filter 或 Servlet 被调用。在这种情况下,Filter 通常负责构造HttpServletResponse 响应客户端.
  • 修改 HttpServletRequest 或者 HttpServletResponse 内容

由于 Filter 是按顺序处理请求,因此顺序对于正确完成一个请求的处理是非常重要。

2.DelegatingFilterProxy

Spring 提供了一个Filter名为DelegatingFilterProxy的实现。这个 Filter 在 Servlet 容器的生命周期和 Spring 的 ApplicationContext 之间建立了桥接。Servlet 容器用自己的标准注册 Filter,但它对 Spring Bean 无感知。 DelegatingFilterProxy 通过标准 Servlet 容器机制注册到 Servlet 中,但将所有工作都委托给了实现 Filter 的 Spring Bean。

下图展示了 DelegatingFilterProxy 在 Filter 和 FilterChain 架构中位置:

image.png

DelegatingFilterProxy 从 Spring 容器 ApplicationContext 中查找 Bean Filter,然后调用。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);
}

3.FilterChainProxy

Spring Security 对 Servlet 的支持包含在 FilterChainProxy。 FilterChainProxy 是 Spring Security 提供的一个特殊的 Filter。它通过过滤功能代理给 SecurityFilterChain 维护的一组 Filter 链。

image.png

4.SecurityFilterChain

FilterChainProxy 利用 SecurityFilterChain 来确定应该为一个请求调用哪些 Spring Security 的 Filter。

image.png

SecurityFilterChain 中的 Filter 是 Spring Bean。它们是注册 FilterChainProxy 中,而不是在 DelegatingFilterProxy 注册的。相比较直接向 Servlet 容器或 DelegatingFilterProxy 注册,FilterChainProxy 有许多优势。

  • 首先,它为 Spring Security 提供了一个起点。如果您尝试对 Spring Security 进行故障 debug,那么在 FilterChainProxy 是个合适调试断点。

  • 其次,由于 FilterChainProxy 是 Spring Security 的核心,它可以执行一些关键任务。例如,它可以清除 SecurityContext 以避免内存泄漏。它还可以用 Spring Security HttpFirewall 来保护应用免受某些类型的攻击。

  • 此外,它在确定何时调用一个 SecurityFilterChain 方面提供了更大的灵活性。在 Servlet 容器中,Filter 只能根据 URL 模式匹配调用。但是,FilterChainProxy 可以使用 RequestMatcher 来匹配 HttpServletRequest 中任何内容来调用。

FilterChainProxy 可以确定调用哪个 SecurityFilterChain。我们可以利用这个特性为应用的不同部分提供完全独立的配置。

image.png

上图有多个 SecurityFilterChain, FilterChainProxy 决定应该使用哪个 SecurityFilterChain。第一个匹配的 SecurityFilterChain 会被调用。如果请求 URL 是 /api/messages/,它将首先匹配上第一个SecurityFilterChain SecurityFilterChain0 的/api/** 模式,虽然 第 n 个 SecurityFilterChain 也能够匹配上 SecurityFilterChain0 是第一个匹配的。因此,SecurityFilterChain0 会被调用。

请注意,上图中 SecurityFilterChain0 仅配置了三个 security Filter 实例。而 第 n 个SecurityFilterChain 配置了四个 security Filter 实例。重要的是要注意,每个 SecurityFilterChain 都可以是唯一的并且可以单独配置。实际上,如果应用程序希望 Spring Security 忽略某些请求,则可以不给 个SecurityFilterChain 配置 security Filter 实例。

5.Security Filters

Security Filter 是通过 SecurityFilterChain API 被插入到 FilterChainProxy 中的。Filter 的顺序是有意义的。通常我们不需要知道 Spring Security Filter 的顺序。但是,有时知道排序是有益的。

以下是 Spring Security Filter 按顺序的完整列表:

  • ForceEagerSessionCreationFilter
  • 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

6.处理安全异常

ExceptionTranslationFilter 是Srping Security 提供的一个 Filter。ExceptionTranslationFilter 可以将 AccessDeniedException 和 AuthenticationException 转换为 HTTP 响应。

image.png

  • 1号首先,ExceptionTranslationFilter调用 FilterChain.doFilter(request, response) 来调用执行应用的其余部分。

  • 2号如果用户未通过身份验证或遇到了一个 AuthenticationException 异常,则开始身份验证

    • 清除 SecurityContextHolder
    • 将 HttpServletRequest 保存在 RequestCache 中。当用户成功认证后,RequestCache 将被用于重放原始请求。
    • AuthenticationEntryPoint 用于从客户端获取凭据。例如,它可能会将用户重定向到登录页面或发送 WWW-Authenticate 请求头。
  • 3 号 如果是 AccessDeniedException,则拒绝访问。AccessDeniedHandler 会被调用来处理拒绝访问。

如果应用程序没有抛出 AccessDeniedException 异常或 AuthenticationException 异常,ExceptionTranslationFilter 则不执行任何操作。

ExceptionTranslationFilter 的伪代码如下所示:

try {
	filterChain.doFilter(request, response);    //1
} catch (AccessDeniedException | AuthenticationException ex) {
	if (!authenticated || ex instanceof AuthenticationException) {
		startAuthentication();              //2
	} else {
		accessDenied();                     //3
	}
}
    1. filterChain.doFilter(request, response) 表示继续执行请求处理,如果处理过程中有抛出一个 AuthenticationException 或者 AccessDeniedException 异常,异常将会被捕获和处理。
    1. 如果用户没有做过身份验证,或者发生了 AuthenticationException 异常,则开开始身份验证逻辑
    1. 否则执行拒绝访问逻辑