# Spring Security Servlet 实现原理

Spring Security 对 Servlet 的支持是基于 `javax.servlet.Filter` 实现的。

## Javax Servlet - Filters

在 Servlet 应用中，一次 HTTP 请求的分层处理如下图所示：

![filter chain](/files/HUy1bESadHDYOVwHx2U8)

Client 给应用发送一个 HTTP 请求，Servlet 容器会创建一个包含 `Filter` 和 `Servlet` 的 `FilterChain`，`FilterChain` 会基于请求 URI 中的 path 部分来处理对应的 `HttpServletRequest`。在 Spring MVC 应用中， `Servlet` 具体是 `org.springframework.web.servlet.DispatcherServlet`。

一个 `Servlet` 最多可以处理一次 `HttpServletRequest` 和 `HttpServletResponse`，但是 `Filter` 可以被使用多次，可以用于：

* 防止下游的 `Filter` 或者 `Servlet` 被调用；
* 修改下游的 `Filter` 和 `Servlet` 所使用的 `HttpServletRequest` 或者 `HttpServletResponse`。

## Spring Web MVC - DelegatingFilterProxy

Spring Web MVC 提供了一个名为 `DelegatingFilterProxy` 的 `Filter` 实现类，**用于连接 Servlet 容器和 Spring 的 `ApplicationContext`**。Servlet 容器只允许注册标准的 `Filter` 实现类，不能注册 Spring IoC 容器中定义的 Bean。`DelegatingFilterProxy` 实现了 `Filter` 接口，可以被标准的 Servlet 容器所注册，并且 **可以把 `Filter` 的实现逻辑委托给一个实现了 `Filter` 接口的 Spring Bean**。

![将 Filter 的实现委托给一个 Spring Bean](/files/EMy1u7KLb7YKfpMTkApM)

`DelegatingFilterProxy` 的工作机制如下图所示：

![delegating filter proxy](/files/0DSYf8ZrVhZj83t6OQIz)

`DelegatingFilterProxy` 会在 Spring `ApplicationContext` 中查找 Bean Filter0，然后调用 Bean Filter0 中的 `Filter` 实现逻辑。

## Spring Security - FilterChainProxy

Spring Security 提供了一个名为 `FilterChainProxy` 的特殊的 `Filter` 实现类，**可以通过 `SecurityFilterChain` 来委托多个 Filter 的实例**。`FilterChainProxy` 属于 Spring Bean，会被包装在 `DelegatingFilterProxy` 中。

`FilterChainProxy` 的工作机制如下图所示：

![filter chain proxy](/files/eP3BujsdaDPjtRxuJd5v)

## Spring Security - SecurityFilterChain

`FilterChainProxy` 使用 `SecurityFilterChain` 来 **确定在当前的 HTTP 请求中应该调用那些 Spring Security 相关的 `Filter`**。

SecurityFilterChain 的工作机制如下图所示：

![security filter chain](/files/o2IT3joEAejJvfpCJWru)

`SecurityFilterChain` 中 Spring Security 相关的 `Filter` 都属于 Spring Bean，会被注册到 `FilterChainProxy` 而不是 `DelegatingFilterProxy` 中。选择将 `Filter` 注册到 `FilterChainProxy`，而不是直接注册到 Servlet 容器或者 `DelegatingFilterProxy` 会有许多好处。首先，**`FilterChainProxy` 为 Spring Security 对 Servlet 的所有支持提供了一个起点**。因此，如果开发者在尝试解决应用中 Spring Security 相关问题的时候，可以在 `FilterChainProxy` 中添加一个断点然后开始 debug。

其次，`FilterChainProxy` 对于 Spring Security 的工作机制来说非常重要，它还被设计成 **可以执行一些辅助性质的任务**。例如，`FilterChainProxy` 会清除 `SecurityContext` 中的数据，用于避免内存泄漏；`FilterChainProxy` 还会通过使用 Spring Security 的 `HttpFirewall` 来保护应用免受某些类型的攻击。

![清除 SecurityContext 中的数据，用于避免内存泄漏](/files/3AbZ3ofpiE4tmAYV4pqr)

![使用 HttpFirewall 来保护应用免受某些类型的攻击](/files/FjY1I3BMBjvAMoBs8jZG)

另外，`FilterChainProxy` 提供了更大的灵活性，**用于确定应该在何时需要调用 `SecurityFilterChain`**。在 Servlet 容器中，仅会单独根据 URL 是否匹配来确定是否需要调用 `Filter`。然而，通过使用 `RequestMatcher` 接口，`FilterChainProxy` 可以基于 `HttpServletRequest` 中的任何内容来确定是否需要调用 `Filter`。

![使用 RequestMatcher 接口来确定是否需要调用 Filter](/files/dCQHbJ9PL1OjDL1BqvHP)

## 总结

`WebSecurityConfiguration` 创建并配置 `WebSecurity`，`WebSecurity` 用于构建 `FilterChainProxy`。

## 参考资料

* [Spring Security - Servlet Security](https://docs.spring.io/spring-security/site/docs/5.3.2.RELEASE/reference/html5/#servlet-architecture)


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://gitbook.fantasticmao.cn/tech/java/spring/spring-security-servlet-shi-xian-yuan-li.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
