# 建筑
本节讨论 Spring 基于 Servlet 的应用程序中的 Spring 安全性的高级体系结构。我们在参考的[认证](authentication/index.html#servlet-authentication)、[授权](authorization/index.html#servlet-authorization)、[保护免受剥削](exploits/index.html#servlet-exploits)部分中建立了这种高层次的理解。
## 《`Filter`s》评介
Spring 安全性的 Servlet 支持是基于 Servlet `Filter`s 的,因此通常首先查看`Filter`s 的作用是有帮助的。下图显示了单个 HTTP 请求的处理程序的典型分层。
![滤清链](https://docs.spring.io/spring-security/reference/_images/servlet/architecture/filterchain.png)
图 1。滤清链
客户机向应用程序发送一个请求,容器创建一个`FilterChain`,其中包含`Filter`s 和`Servlet`,它们应该基于请求 URI 的路径来处理`HttpServletRequest`。在 Spring MVC 应用程序中,`Servlet`是[`DispatcherServlet`](https://DOCS. Spring.io/ Spring-framework/DOCS/5.3.16/reference/html/web.html#mvc- Servlet)的一个实例。最多一个`Servlet`可以处理单个`HttpServletRequest`和`HttpServletResponse`。但是,可以使用多个`Filter`来:
* 阻止调用下游`Filter`s 或`Servlet`。在这种情况下,`Filter`通常会写`HttpServletResponse`。
* 修改由下游`Filter`s 和`Servlet`使用的`HttpServletRequest`或`HttpServletResponse`
`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`只会影响下游`Filter`s 和`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 容器允许使用自己的标准注册`Filter`s,但它不知道 Spring 定义的 bean。`DelegatingFilterProxy`可以通过标准 Servlet 容器机制注册,但将所有工作委托给实现`Filter`的 Spring Bean。
下面是一张`DelegatingFilterProxy`如何与[`Filter`s 和`FilterChain`](# Servlet-filters-review)相匹配的图片。
![委托过滤代理](https://docs.spring.io/spring-security/reference/_images/servlet/architecture/delegatingfilterproxy.png)
图 2。委托过滤代理
`DelegatingFilterProxy`从`ApplicationContext`中查找*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,因此它通常被包装在[委托过滤代理](#servlet-delegatingfilterproxy)中。
![FilterchainProxy ](https://docs.spring.io/spring-security/reference/_images/servlet/architecture/filterchainproxy.png)
图 3。FilterchainProxy
## SecurityFilterChain
[`SecurityFilterChain`](https://DOCS. Spring.io/ Spring-security/site/DOCS/5.6.2/api/org/springframework/security/web/securityfilterchain.html)被[FilterchainProxy ](#servlet-filterchainproxy)用于确定应该为此请求调用哪个 Spring security`Filter`s。
![证券过滤链](https://docs.spring.io/spring-security/reference/_images/servlet/architecture/securityfilterchain.png)
图 4。证券过滤链
[安全过滤器](#servlet-security-filters)中的`SecurityFilterChain`通常是 bean,但它们是用`FilterChainProxy`而不是[委托过滤代理](#servlet-delegatingfilterproxy)注册的。`FilterChainProxy`提供了直接向 Servlet 容器或[委托过滤代理](#servlet-delegatingfilterproxy)注册的许多优点。首先,它为 Spring Security 的 Servlet 支持提供了一个起点。因此,如果你试图对 Spring Security 的 Servlet 支持进行故障排除,那么在`FilterChainProxy`中添加一个调试点是一个很好的起点。
其次,由于`FilterChainProxy`是 Spring 安全性使用的核心,因此它可以执行不被视为可选的任务。例如,它清除`SecurityContext`以避免内存泄漏。它还应用 Spring Security 的[`HttpFirewall`](exploits/firewall.html# Servlet-HttpFirewall)来保护应用程序免受某些类型的攻击。
此外,它在确定何时应该调用`SecurityFilterChain`时提供了更大的灵活性。在 Servlet 容器中,`Filter`s 仅基于 URL 被调用。但是,`FilterChainProxy`可以通过利用`RequestMatcher`接口,基于`HttpServletRequest`中的任何内容来确定调用。
事实上,`FilterChainProxy`可以用来确定应该使用哪些`SecurityFilterChain`。这允许为应用程序的不同*切片*提供完全独立的配置。
![多证券过滤链](https://docs.spring.io/spring-security/reference/_images/servlet/architecture/multi-securityfilterchain.png)
图 5。多重证券过滤链
在[多重证券过滤链](#servlet-multi-securityfilterchain-figure)图中,`FilterChainProxy`决定应该使用哪个`SecurityFilterChain`。只调用第一个匹配的`SecurityFilterChain`。如果请求`/api/messages/`的 URL,它将首先匹配`SecurityFilterChain0`的`/api/**`的模式,因此只有`SecurityFilterChain0`将被调用,即使它也匹配`SecurityFilterChainn`。如果请求一个`/messages/`的 URL,它将不匹配`SecurityFilterChain0`的`/api/**`的模式,因此`FilterChainProxy`将继续尝试每个`SecurityFilterChain`。假设没有其他的,`SecurityFilterChain`实例匹配`SecurityFilterChainn`将被调用。
请注意,`SecurityFilterChain0`只配置了三个安全`Filter`实例。但是,`SecurityFilterChainn`配置了四个`Filter`的安全性。需要注意的是,每个`SecurityFilterChain`都可以是唯一的,并且可以单独配置。实际上,如果应用程序希望 Spring 安全性忽略某些请求,则`SecurityFilterChain`可能具有零安全性`Filter`s。
## 安全过滤器
使用[证券过滤链](#servlet-securityfilterchain)API 将安全过滤器插入[FilterchainProxy ](#servlet-filterchainproxy)中。[`Filter`](# Servlet-过滤器-评审)的顺序很重要。通常不需要知道 Spring 证券的`Filter`s 的排序。
下面是 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`作为[安全过滤器](#servlet-security-filters)中的一个插入到[FilterchainProxy ](#servlet-filterchainproxy)中。
![ExceptionTranslationFilter ](https://docs.spring.io/spring-security/reference/_images/servlet/architecture/exceptiontranslationfilter.png)
* ![number 1](https://docs.spring.io/spring-security/reference/_images/icons/number_1.png)首先,`ExceptionTranslationFilter`调用`FilterChain.doFilter(request, response)`来调用应用程序的其余部分。
* ![number 2](https://docs.spring.io/spring-security/reference/_images/icons/number_2.png)如果用户没有经过身份验证,或者它是`AuthenticationException`,那么*启动身份验证*。
* [SecurityContextholder ](authentication/architecture.html#servlet-authentication-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](https://docs.spring.io/spring-security/reference/_images/icons/number_3.png)否则如果是`AccessDeniedException`,则*访问被拒绝*。调用`AccessDeniedHandler`来处理拒绝访问。
| |如果应用程序不抛出`AccessDeniedException`或`AuthenticationException`,则`ExceptionTranslationFilter`不执行任何操作。|
|---|-----------------------------------------------------------------------------------------------------------------------------------------------------|
`ExceptionTranslationFilter`的伪代码如下所示:
ExceptionTranslationFilter 伪码
```
try {
filterChain.doFilter(request, response); (1)
} catch (AccessDeniedException | AuthenticationException ex) {
if (!authenticated || ex instanceof AuthenticationException) {
startAuthentication(); (2)
} else {
accessDenied(); (3)
}
}
```
|**1**|你可以从[对`Filter`s 的回顾](# Servlet-filters-review)中回忆起,调用`FilterChain.doFilter(request, response)`相当于调用应用程序的其余部分。
这意味着,如果应用程序的另一部分,(即[`FilterSecurityInterceptor`](Authorization/authorization-requests.html# Servlet-authorization-filterSecurityInterceptor)或方法安全性)抛出一个`AuthenticationException`或`AccessDeniedException`将在此捕获并处理。|
|-----|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|**2**|如果用户没有经过身份验证,或者它是`AuthenticationException`,那么*启动身份验证*。|
|**3**|否则,*访问被拒绝*|
[开始](getting-started.html)[认证](authentication/index.html)