此版本仍在开发中,尚未被视为稳定版本。对于最新的稳定版本,请使用 Spring Security 6.4.3spring-doc.cadn.net.cn

架构

本节讨论 Spring Security 在基于 Servlet 的应用程序中的高级体系结构。 我们在参考的 AuthenticationAuthorizationProtection Against Exploits 部分建立了这种高层次的理解。spring-doc.cadn.net.cn

回顾Filters

Spring Security 的 Servlet 支持基于 ServletFilters 中,因此查看Filters 通常排在第一位。 下图显示了单个 HTTP 请求的处理程序的典型分层。spring-doc.cadn.net.cn

filterchain (筛选链)
图 1.FilterChain (筛选链)

客户端向应用程序发送请求,容器创建一个FilterChain其中包含Filters 和Servlet它应该处理HttpServletRequest基于请求 URI 的路径。 在 Spring MVC 应用程序中,ServletDispatcherServlet. 最多一个Servlet可以处理单个HttpServletRequestHttpServletResponse. 但是,不止一个Filter可用于:spring-doc.cadn.net.cn

  • 防止下游Filters 或Servlet免于被调用。 在本例中,Filter通常会写入HttpServletResponse.spring-doc.cadn.net.cn

  • 修改HttpServletRequestHttpServletResponse由下游使用Filters 和Servletspring-doc.cadn.net.cn

的强大功能Filter来自FilterChain这被传递到它里面。spring-doc.cadn.net.cn

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
}
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-doc.cadn.net.cn

委托过滤器代理

Spring 提供了一个Filter名为DelegatingFilterProxy这允许在 Servlet 容器的生命周期和 Spring 的生命周期之间架起桥梁ApplicationContext. Servlet 容器允许注册Filter使用自己的标准,但它不知道 Spring 定义的 Bean。DelegatingFilterProxy可以通过标准的 Servlet 容器机制进行注册,但将所有工作委托给实现Filter.spring-doc.cadn.net.cn

这是一张如何作的图片DelegatingFilterProxy适合Filters 和FilterChain.spring-doc.cadn.net.cn

DelegatingFilterProxy
图 2.委托过滤器代理

DelegatingFilterProxy查找Bean 过滤器0ApplicationContext然后调用Bean 过滤器0. 的伪代码DelegatingFilterProxy可以在下面看到。spring-doc.cadn.net.cn

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);
}
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是它允许延迟查找Filterbean 实例。 这很重要,因为容器需要注册Filter实例。 但是, Spring 通常使用ContextLoaderListener加载 Spring Bean,这只有在Filter需要注册实例。spring-doc.cadn.net.cn

FilterChainProxy

Spring Security 的 Servlet 支持包含在FilterChainProxy.FilterChainProxy是一种特殊的Filter由 Spring Security 提供,允许委托给多个Filter实例SecurityFilterChain. 因为FilterChainProxy是一个 Bean,它通常包装在 DelegatingFilterProxy 中。spring-doc.cadn.net.cn

filterchainproxy
图 3.FilterChainProxy

SecurityFilterChain 安全过滤器链

SecurityFilterChainFilterChainProxy 用于确定哪个 Spring SecurityFilters 的请求。spring-doc.cadn.net.cn

SecurityFilterChain 安全过滤器链
图 4.SecurityFilterChain 安全过滤器链

安全过滤器SecurityFilterChain通常是 Bean,但它们是使用FilterChainProxy而不是 DelegatingFilterProxyFilterChainProxy为直接向 Servlet 容器或 DelegatingFilterProxy 注册提供了许多优势。 首先,它为 Spring Security 的所有 Servlet 支持提供了一个起点。 因此,如果您尝试对 Spring Security 的 Servlet 支持进行故障排除,请在FilterChainProxy是一个很好的起点。spring-doc.cadn.net.cn

其次,由于FilterChainProxy是 Spring Security 使用的核心,它可以执行不被视为可选的任务。 例如,它会清除SecurityContext以避免内存泄漏。 它还应用 Spring Security 的HttpFirewall保护应用程序免受某些类型的攻击。spring-doc.cadn.net.cn

此外,它还在确定何时SecurityFilterChain应该调用。 在 Servlet 容器中,Filter仅根据 URL 调用。 然而FilterChainProxy可以根据HttpServletRequest通过利用RequestMatcher接口。spring-doc.cadn.net.cn

事实上FilterChainProxy可用于确定哪个SecurityFilterChain应该使用。 这允许为应用程序的不同切片提供完全独立的配置。spring-doc.cadn.net.cn

Multi SecurityFilterChain
图 5.多个 SecurityFilterChain

在多个 SecurityFilterChainFilterChainProxy决定哪个SecurityFilterChain应该使用。 只有第一个SecurityFilterChain的匹配项。 如果 URL 为/api/messages/请求时,它将首先匹配SecurityFilterChain0的模式/api/**,所以只有SecurityFilterChain0将被调用,即使它也匹配SecurityFilterChainn. 如果 URL 为/messages/时,它不会匹配SecurityFilterChain0的模式/api/**所以FilterChainProxy将继续尝试每个SecurityFilterChain. 假设没有其他人,SecurityFilterChain实例匹配SecurityFilterChainn将被调用。spring-doc.cadn.net.cn

请注意,SecurityFilterChain0只有三个安全Filters 实例。 然而SecurityFilterChainn有四项安全保障Filter已配置。 需要注意的是,每个SecurityFilterChain可以是唯一的,并且可以单独配置。 实际上,SecurityFilterChain可能具有零安全性Filter如果应用程序希望 Spring Security 忽略某些请求,则 sspring-doc.cadn.net.cn

安全过滤器

安全筛选器使用 SecurityFilterChain API 插入到 FilterChainProxy 中。 这顺序Filter很重要。 通常不需要知道 Spring Security 的Filters. 但是,有时了解顺序是有益的spring-doc.cadn.net.cn

以下是 Spring Security 过滤器订购的完整列表:spring-doc.cadn.net.cn

处理安全异常

ExceptionTranslationFilter作为安全筛选器之一插入到 FilterChainProxy 中。spring-doc.cadn.net.cn

exceptiontranslationfilter

如果应用程序没有抛出AccessDeniedExceptionAuthenticationException然后ExceptionTranslationFilter不执行任何作。spring-doc.cadn.net.cn

的伪代码ExceptionTranslationFilter看起来像这样:spring-doc.cadn.net.cn

ExceptionTranslationFilter 伪代码
try {
	filterChain.doFilter(request, response); (1)
} catch (AccessDeniedException | AuthenticationException ex) {
	if (!authenticated || ex instanceof AuthenticationException) {
		startAuthentication(); (2)
	} else {
		accessDenied(); (3)
	}
}
1 您将回忆起回顾Filters那个祈求者FilterChain.doFilter(request, response)等效于调用应用程序的其余部分。 这意味着,如果应用程序的另一部分(即FilterSecurityInterceptor或方法安全性)会抛出一个AuthenticationExceptionAccessDeniedException它将在这里被捕获和处理。
2 如果用户未经过身份验证或用户是AuthenticationException,然后单击 Start Authentication (开始身份验证)。
3 否则,Access Denied (访问被拒绝