# 建筑

本节讨论 Spring 基于 Servlet 的应用程序中的 Spring 安全性的高级体系结构。我们在参考的认证授权保护免受剥削部分中建立了这种高层次的理解。

#Filters》评介

Spring 安全性的 Servlet 支持是基于 Servlet Filters 的,因此通常首先查看Filters 的作用是有帮助的。下图显示了单个 HTTP 请求的处理程序的典型分层。

滤清链

图 1。滤清链

客户机向应用程序发送一个请求,容器创建一个FilterChain,其中包含Filters 和Servlet,它们应该基于请求 URI 的路径来处理HttpServletRequest。在 Spring MVC 应用程序中,Servlet是[DispatcherServlet](https://DOCS. Spring.io/ Spring-framework/DOCS/5.3.16/reference/html/web.html#mvc- Servlet)的一个实例。最多一个Servlet可以处理单个HttpServletRequestHttpServletResponse。但是,可以使用多个Filter来:

  • 阻止调用下游Filters 或Servlet。在这种情况下,Filter通常会写HttpServletResponse

  • 修改由下游Filters 和Servlet使用的HttpServletRequestHttpServletResponse

Filter的幂来自传递到它的FilterChain

例 1。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
}

Kotlin

fun doFilter(request: ServletRequest, response: ServletResponse, chain: FilterChain) {
    // 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的顺序是极其重要的。

# 委托过滤代理

Spring 提供了名为[Filter](https://DOCS. Spring.io/ Spring-framework/DOCS/5.3.16/javadoc-api/org/springframework/web/filter/delegatingfilterproxy.html)的Filter实现,该实现允许在 Servlet 容器的生命周期和 Spring 的ApplicationContext之间架桥。 Servlet 容器允许使用自己的标准注册Filters,但它不知道 Spring 定义的 bean。DelegatingFilterProxy可以通过标准 Servlet 容器机制注册,但将所有工作委托给实现Filter的 Spring Bean。

下面是一张DelegatingFilterProxy如何与[Filters 和FilterChain](# Servlet-filters-review)相匹配的图片。

委托过滤代理

图 2。委托过滤代理

DelegatingFilterProxyApplicationContext中查找Bean Filter0,然后调用Bean Filter0。下面可以看到DelegatingFilterProxy的伪代码。

例 2。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);
}

Kotlin

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

DelegatingFilterProxy的另一个好处是,它允许延迟查找Filter Bean 实例。这一点很重要,因为容器在启动之前需要注册Filter实例。然而, Spring 通常使用ContextLoaderListener来加载 Spring bean,这在Filter实例需要注册之后才能完成。

# FilterchainProxy

Spring Security 的 Servlet 支持包含在FilterChainProxy中。FilterChainProxy是 Spring Security 提供的一种特殊的Filter,它允许通过[证券过滤链](# Servlet-SecurityFilterchain)将许多Filter实例委托给多个实例。由于FilterChainProxy是 Bean,因此它通常被包装在委托过滤代理中。

FilterchainProxy

图 3。FilterchainProxy

# SecurityFilterChain

[SecurityFilterChain](https://DOCS. Spring.io/ Spring-security/site/DOCS/5.6.2/api/org/springframework/security/web/securityfilterchain.html)被FilterchainProxy 用于确定应该为此请求调用哪个 Spring securityFilters。

证券过滤链

图 4。证券过滤链

安全过滤器中的SecurityFilterChain通常是 bean,但它们是用FilterChainProxy而不是委托过滤代理注册的。FilterChainProxy提供了直接向 Servlet 容器或委托过滤代理注册的许多优点。首先,它为 Spring Security 的 Servlet 支持提供了一个起点。因此,如果你试图对 Spring Security 的 Servlet 支持进行故障排除,那么在FilterChainProxy中添加一个调试点是一个很好的起点。

其次,由于FilterChainProxy是 Spring 安全性使用的核心,因此它可以执行不被视为可选的任务。例如,它清除SecurityContext以避免内存泄漏。它还应用 Spring Security 的[HttpFirewall](exploits/firewall.html# Servlet-HttpFirewall)来保护应用程序免受某些类型的攻击。

此外,它在确定何时应该调用SecurityFilterChain时提供了更大的灵活性。在 Servlet 容器中,Filters 仅基于 URL 被调用。但是,FilterChainProxy可以通过利用RequestMatcher接口,基于HttpServletRequest中的任何内容来确定调用。

事实上,FilterChainProxy可以用来确定应该使用哪些SecurityFilterChain。这允许为应用程序的不同切片提供完全独立的配置。

多证券过滤链

图 5。多重证券过滤链

多重证券过滤链图中,FilterChainProxy决定应该使用哪个SecurityFilterChain。只调用第一个匹配的SecurityFilterChain。如果请求/api/messages/的 URL,它将首先匹配SecurityFilterChain<sub>0</sub>/api/**的模式,因此只有SecurityFilterChain<sub>0</sub>将被调用,即使它也匹配SecurityFilterChain<sub>n</sub>。如果请求一个/messages/的 URL,它将不匹配SecurityFilterChain<sub>0</sub>/api/**的模式,因此FilterChainProxy将继续尝试每个SecurityFilterChain。假设没有其他的,SecurityFilterChain实例匹配SecurityFilterChain<sub>n</sub>将被调用。

请注意,SecurityFilterChain<sub>0</sub>只配置了三个安全Filter实例。但是,SecurityFilterChain<sub>n</sub>配置了四个Filter的安全性。需要注意的是,每个SecurityFilterChain都可以是唯一的,并且可以单独配置。实际上,如果应用程序希望 Spring 安全性忽略某些请求,则SecurityFilterChain可能具有零安全性Filters。

# 安全过滤器

使用证券过滤链API 将安全过滤器插入FilterchainProxy 中。[Filter](# Servlet-过滤器-评审)的顺序很重要。通常不需要知道 Spring 证券的Filters 的排序。

下面是 Spring 安全过滤器排序的综合列表:

  • 通道处理滤波器

  • WebAsyncManagerIntegrationFilter

  • SecurityContextPersistenceFilter

  • HeaderWriterFilter

  • Corsfilter

  • Csrffilter

  • LogoutFilter

  • OAuth2AuthorizationRequestReDirectFilter

  • SAML2WebssoAuthenticationRequestFilter

  • X509 身份验证过滤器

  • 抽象预先验证的处理过滤器

  • CasauthenticationFilter

  • OAuth2 登录验证过滤器

  • SAML2WebssoAuthenticationFilter

  • [UsernamePasswordAuthenticationFilter](身份验证/密码/form.html# Servlet-身份验证-usernamepasswordauthenticationfilter)

  • openidauthenticationfilter

  • DefaultLoginPageGeneratingFilter

  • DefaultLogOutpageGeneratingFilter

  • ConcurrentSessionFilter

  • [DigestAuthenticationFilter](authentication/passwords/digest.html# Servlet-authentication-digest)

  • BeareRtoKenAuthenticationFilter

  • [BasicAuthenticationFilter](authentication/passwords/basic.html# Servlet-authentication-basic)

  • RequestCacheAwareFilter

  • SecurityContextholderAwareRequestFilter

  • JaasapiIntegrationfilter

  • RememerMeAuthenticationFilter

  • 匿名身份验证过滤器

  • OAuth2AuthorizationCodeGrantFilter

  • SessionManagementFilter

  • [ExceptionTranslationFilter](# Servlet-ExceptionTranslationFilter)

  • [FilterSecurityInterceptor](授权/authorization-requests.html# Servlet-authorization-filtersecurityinterceptor)

  • 切换 chuserfilter

# 处理安全异常

[ExceptionTranslationFilter](https://DOCS. Spring.io/ Spring-security/site/DOCS/5.6.2/api/org/springframework/security/web/access/excess/exceptiontranslationfilter.html)允许将[AccessDeniedException](https://DOCS. Spring.io/ Spring.io/ Spring-cesecurity/site/DOCS/5.6.2/api/api/org/accessframework/secframework/securance/securance/securation/acception/acc

ExceptionTranslationFilter作为安全过滤器中的一个插入到FilterchainProxy 中。

ExceptionTranslationFilter

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

  • number 2如果用户没有经过身份验证,或者它是AuthenticationException,那么启动身份验证

    • SecurityContextholder 已清除

    • HttpServletRequest保存在[RequestCache](https://DOCS. Spring.io/ Spring-security/site/DOCS/5.6.2/api/org/springframework/security/web/savedrequest/requestcache.html)中。当用户成功进行身份验证时,将使用RequestCache重播原始请求。

    • AuthenticationEntryPoint用于从客户机请求凭据。例如,它可能重定向到一个登录页面,或者发送一个WWW-Authenticate头。

  • number 3否则如果是AccessDeniedException,则访问被拒绝。调用AccessDeniedHandler来处理拒绝访问。

如果应用程序不抛出AccessDeniedExceptionAuthenticationException,则ExceptionTranslationFilter不执行任何操作。

ExceptionTranslationFilter的伪代码如下所示:

ExceptionTranslationFilter 伪码

try {
	filterChain.doFilter(request, response); (1)
} catch (AccessDeniedException | AuthenticationException ex) {
	if (!authenticated || ex instanceof AuthenticationException) {
		startAuthentication(); (2)
	} else {
		accessDenied(); (3)
	}
}
1 你可以从[对Filters 的回顾](# Servlet-filters-review)中回忆起,调用FilterChain.doFilter(request, response)相当于调用应用程序的其余部分。
这意味着,如果应用程序的另一部分,(即[FilterSecurityInterceptor](Authorization/authorization-requests.html# Servlet-authorization-filterSecurityInterceptor)或方法安全性)抛出一个AuthenticationExceptionAccessDeniedException将在此捕获并处理。
2 如果用户没有经过身份验证,或者它是AuthenticationException,那么启动身份验证
3 否则,访问被拒绝

开始认证