对于最新的稳定版本,请使用 Spring Security 6.4.1spring-doc.cn

架构

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

A 综述Filter

Spring Security 的 Servlet 支持基于 Servlet s,因此通常首先查看 s 的角色是有帮助的。 下图显示了单个 HTTP 请求的处理程序的典型分层。FilterFilterspring-doc.cn

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

客户端向应用程序发送请求,容器创建一个包含 s 的请求,该请求应根据请求 URI 的路径进行处理。 在 Spring MVC 应用程序中,它是DispatcherServlet的实例。 最多只能处理单个 和 。 但是,可以使用多个 API 来:FilterChainFilterServletHttpServletRequestServletServletHttpServletRequestHttpServletResponseFilterspring-doc.cn

  • 防止下游 s 或 the 被调用。 在这种情况下,通常会写入 .FilterServletFilterHttpServletResponsespring-doc.cn

  • 修改下游 s 和HttpServletRequestHttpServletResponseFilterServletspring-doc.cn

力量来自传递给它的东西。FilterFilterChainspring-doc.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
}

由于 a 仅影响下游 s 和 ,因此每个 的调用顺序非常重要。FilterFilterServletFilterspring-doc.cn

委托过滤器代理

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

下面是如何适应 FilterFilterChain 的图片。DelegatingFilterProxyspring-doc.cn

DelegatingFilterProxy
图 2.委托过滤器代理

DelegatingFilterProxy查找Bean 过滤器0从 和 然后调用ApplicationContextBean 过滤器0. 的伪代码可以在下面看到。DelegatingFilterProxyspring-doc.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)
}

另一个好处是它允许延迟查找 bean 实例。 这一点很重要,因为容器需要先注册实例,然后才能启动容器。 但是, Spring 通常使用 a 来加载 Spring Bean,直到需要注册实例后才会完成。DelegatingFilterProxyFilterFilterContextLoaderListenerFilterspring-doc.cn

FilterChainProxy

Spring Security 的 Servlet 支持包含在 中。 是 Spring Security 提供的特殊功能,它允许通过SecurityFilterChain委托给许多实例。 由于 是一个 Bean,因此它通常包装在 DelegatingFilterProxy 中。FilterChainProxyFilterChainProxyFilterFilterFilterChainProxyspring-doc.cn

filterchainproxy
图 3.FilterChainProxy

SecurityFilterChain 安全过滤器链

SecurityFilterChainProxy使用FilterChain来确定应该为此请求调用哪些 Spring Security。Filterspring-doc.cn

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

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

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

此外,它还在确定何时应调用 a 方面提供了更大的灵活性。 在 Servlet 容器中,仅根据 URL 调用 s。 但是,可以通过利用该接口根据 中的任何内容来确定调用。SecurityFilterChainFilterFilterChainProxyHttpServletRequestRequestMatcherspring-doc.cn

其实,可以用来决定应该用哪个。 这允许为应用程序的不同切片提供完全独立的配置。FilterChainProxySecurityFilterChainspring-doc.cn

Multi SecurityFilterChain
图 5.多个 SecurityFilterChain

在多个 SecurityFilterChain 中,Figure 决定应该使用哪个。 只会调用第一个匹配的 ID。 如果请求 的 URL,它将首先匹配 的 模式,因此即使它也匹配 ,也只会调用。 如果请求的 URL ,它将与 的 模式不匹配,因此将继续尝试每个 。 假设没有其他实例匹配,则将被调用。FilterChainProxySecurityFilterChainSecurityFilterChain/api/messages/SecurityFilterChain0/api/**SecurityFilterChain0SecurityFilterChainn/messages/SecurityFilterChain0/api/**FilterChainProxySecurityFilterChainSecurityFilterChainSecurityFilterChainnspring-doc.cn

请注意,仅配置了三个 security s 实例。 但是,配置了四个安全 s。 请务必注意,每个 Cookie 都可以是唯一的,并且可以单独配置。 事实上,如果应用程序希望 Spring Security 忽略某些请求,则 a 可能具有零安全性。SecurityFilterChain0FilterSecurityFilterChainnFilterSecurityFilterChainSecurityFilterChainFilterspring-doc.cn

安全过滤器

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

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

处理安全异常

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

exceptiontranslationfilter
  • 数字 1首先,调用 以调用应用程序的其余部分。ExceptionTranslationFilterFilterChain.doFilter(request, response)spring-doc.cn

  • 编号 2如果用户未经过身份验证或用户是 ,则开始身份验证AuthenticationExceptionspring-doc.cn

  • 编号 3否则,如果它是 ,则为 Access Denied。 调用 以处理被拒绝的访问。AccessDeniedExceptionAccessDeniedHandlerspring-doc.cn

如果应用程序不抛出 an 或 an ,则不执行任何操作。AccessDeniedExceptionAuthenticationExceptionExceptionTranslationFilterspring-doc.cn

的伪代码如下所示:ExceptionTranslationFilterspring-doc.cn

ExceptionTranslationFilter 伪代码
try {
	filterChain.doFilter(request, response); (1)
} catch (AccessDeniedException | AuthenticationException ex) {
	if (!authenticated || ex instanceof AuthenticationException) {
		startAuthentication(); (2)
	} else {
		accessDenied(); (3)
	}
}
1 您将从 A Review of Filter 中回想起,调用等效于调用应用程序的其余部分。 这意味着,如果应用程序的另一部分(即FilterSecurityInterceptor或方法安全性)抛出OR,它将在此处被捕获并处理。FilterChain.doFilter(request, response)AuthenticationExceptionAccessDeniedException
2 如果用户未经过身份验证或用户是 ,则开始身份验证AuthenticationException
3 否则,Access Denied (访问被拒绝