# 建筑 本节讨论 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)