此版本仍在开发中,尚未被视为稳定版本。对于最新的稳定版本,请使用 Spring Security 6.4.3! |
架构
本节讨论 Spring Security 在基于 Servlet 的应用程序中的高级体系结构。 我们在参考的 Authentication, Authorization, Protection Against Exploits 部分建立了这种高层次的理解。
回顾Filter
s
Spring Security 的 Servlet 支持基于 ServletFilter
s 中,因此查看Filter
s 通常排在第一位。
下图显示了单个 HTTP 请求的处理程序的典型分层。

客户端向应用程序发送请求,容器创建一个FilterChain
其中包含Filter
s 和Servlet
它应该处理HttpServletRequest
基于请求 URI 的路径。
在 Spring MVC 应用程序中,Servlet
是DispatcherServlet
.
最多一个Servlet
可以处理单个HttpServletRequest
和HttpServletResponse
.
但是,不止一个Filter
可用于:
-
防止下游
Filter
s 或Servlet
免于被调用。 在本例中,Filter
通常会写入HttpServletResponse
. -
修改
HttpServletRequest
或HttpServletResponse
由下游使用Filter
s 和Servlet
的强大功能Filter
来自FilterChain
这被传递到它里面。
FilterChain
使用示例-
Java
-
Kotlin
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
}
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
名为DelegatingFilterProxy
这允许在 Servlet 容器的生命周期和 Spring 的生命周期之间架起桥梁ApplicationContext
.
Servlet 容器允许注册Filter
使用自己的标准,但它不知道 Spring 定义的 Bean。DelegatingFilterProxy
可以通过标准的 Servlet 容器机制进行注册,但将所有工作委托给实现Filter
.
这是一张如何作的图片DelegatingFilterProxy
适合Filter
s 和FilterChain
.

DelegatingFilterProxy
查找Bean 过滤器0从ApplicationContext
然后调用Bean 过滤器0.
的伪代码DelegatingFilterProxy
可以在下面看到。
DelegatingFilterProxy
伪代码-
Java
-
Kotlin
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);
}
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
是一种特殊的Filter
由 Spring Security 提供,允许委托给多个Filter
实例SecurityFilterChain
.
因为FilterChainProxy
是一个 Bean,它通常包装在 DelegatingFilterProxy 中。

SecurityFilterChain 安全过滤器链
SecurityFilterChain
被 FilterChainProxy 用于确定哪个 Spring SecurityFilter
s 的请求。

安全过滤器SecurityFilterChain
通常是 Bean,但它们是使用FilterChainProxy
而不是 DelegatingFilterProxy。FilterChainProxy
为直接向 Servlet 容器或 DelegatingFilterProxy 注册提供了许多优势。
首先,它为 Spring Security 的所有 Servlet 支持提供了一个起点。
因此,如果您尝试对 Spring Security 的 Servlet 支持进行故障排除,请在FilterChainProxy
是一个很好的起点。
其次,由于FilterChainProxy
是 Spring Security 使用的核心,它可以执行不被视为可选的任务。
例如,它会清除SecurityContext
以避免内存泄漏。
它还应用 Spring Security 的HttpFirewall
保护应用程序免受某些类型的攻击。
此外,它还在确定何时SecurityFilterChain
应该调用。
在 Servlet 容器中,Filter
仅根据 URL 调用。
然而FilterChainProxy
可以根据HttpServletRequest
通过利用RequestMatcher
接口。
事实上FilterChainProxy
可用于确定哪个SecurityFilterChain
应该使用。
这允许为应用程序的不同切片提供完全独立的配置。

在多个 SecurityFilterChain 图FilterChainProxy
决定哪个SecurityFilterChain
应该使用。
只有第一个SecurityFilterChain
的匹配项。
如果 URL 为/api/messages/
请求时,它将首先匹配SecurityFilterChain0
的模式/api/**
,所以只有SecurityFilterChain0
将被调用,即使它也匹配SecurityFilterChainn
.
如果 URL 为/messages/
时,它不会匹配SecurityFilterChain0
的模式/api/**
所以FilterChainProxy
将继续尝试每个SecurityFilterChain
.
假设没有其他人,SecurityFilterChain
实例匹配SecurityFilterChainn
将被调用。
请注意,SecurityFilterChain0
只有三个安全Filter
s 实例。
然而SecurityFilterChainn
有四项安全保障Filter
已配置。
需要注意的是,每个SecurityFilterChain
可以是唯一的,并且可以单独配置。
实际上,SecurityFilterChain
可能具有零安全性Filter
如果应用程序希望 Spring Security 忽略某些请求,则 s
安全过滤器
安全筛选器使用 SecurityFilterChain API 插入到 FilterChainProxy 中。
这顺序Filter
很重要。
通常不需要知道 Spring Security 的Filter
s.
但是,有时了解顺序是有益的
以下是 Spring Security 过滤器订购的完整列表:
-
ChannelProcessingFilter
-
WebAsyncManagerIntegrationFilter
-
SecurityContextPersistenceFilter
-
HeaderWriterFilter
-
CorsFilter 过滤器
-
Csrf过滤器
-
LogoutFilter
-
OAuth2AuthorizationRequestRedirectFilter
-
Saml2WebSsoAuthenticationRequestFilter
-
X509AuthenticationFilter
-
AbstractPreAuthenticatedProcessingFilter
-
CasAuthenticationFilter 的
-
OAuth2LoginAuthenticationFilter
-
Saml2WebSsoAuthenticationFilter
-
OpenIDAuthenticationFilter
-
DefaultLoginPageGeneratingFilter
-
DefaultLogoutPageGeneratingFilter
-
ConcurrentSessionFilter
-
BearerTokenAuthenticationFilter
-
RequestCacheAwareFilter
-
SecurityContextHolderAwareRequestFilter
-
JaasApiIntegrationFilter
-
RememberMeAuthenticationFilter
-
匿名身份验证过滤器
-
OAuth2AuthorizationCodeGrantFilter
-
会话管理过滤器
-
SwitchUserFilter
处理安全异常
这ExceptionTranslationFilter
允许翻译AccessDeniedException
和AuthenticationException
转换为 HTTP 响应。
ExceptionTranslationFilter
作为安全筛选器之一插入到 FilterChainProxy 中。

-
首先,
ExceptionTranslationFilter
调用FilterChain.doFilter(request, response)
以调用应用程序的其余部分。 -
如果用户未经过身份验证或用户是
AuthenticationException
,然后单击 Start Authentication (开始身份验证)。-
这
HttpServletRequest
保存在RequestCache
. 当用户成功进行身份验证时,RequestCache
用于重放原始请求。 -
这
AuthenticationEntryPoint
用于向客户端请求凭据。 例如,它可能会重定向到登录页面或发送WWW-Authenticate
页眉。
-
否则,如果它是
AccessDeniedException
,然后单击 Access Denied。 这AccessDeniedHandler
用于处理 Access Denied。
如果应用程序没有抛出 |
的伪代码ExceptionTranslationFilter
看起来像这样:
try {
filterChain.doFilter(request, response); (1)
} catch (AccessDeniedException | AuthenticationException ex) {
if (!authenticated || ex instanceof AuthenticationException) {
startAuthentication(); (2)
} else {
accessDenied(); (3)
}
}
1 | 您将回忆起回顾Filter s那个祈求者FilterChain.doFilter(request, response) 等效于调用应用程序的其余部分。
这意味着,如果应用程序的另一部分(即FilterSecurityInterceptor 或方法安全性)会抛出一个AuthenticationException 或AccessDeniedException 它将在这里被捕获和处理。 |
2 | 如果用户未经过身份验证或用户是AuthenticationException ,然后单击 Start Authentication (开始身份验证)。 |
3 | 否则,Access Denied (访问被拒绝) |