Spring Security 的体系结构

回顾 Filters

Spring Security 的 Servlet 支持是基于 Severlet 过滤器的,因此通常先看看过滤器的角色是有帮助的。下面的图片显示了单个 HTTP 请求的处理程序的典型分层。
过滤器链
客户机向应用程序发送一个请求,容器创建一个 FilterChain(过滤器链),其中包含过滤器和Servlet,这些过滤器和Servlet应根据请求URI的路径处理HttpServletRequest。一个Servlet可以处理单个HttpServletRequest和HttpServletResponse。但是,可以使用多个过滤器来执行以下操作:

  • 阻止后面的 Filter 或 Servlet 被调用,一般会通过 HttpServletResponse 直接完成响应。
  • 修改后面的 Filter 和Servlet 使用的 HttpServletRequest 或 HttpServletResponse。
    过滤器的功能来自传递给它的过滤器链(FilterChain )
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) {
	// 在请求到达后面Filter和Servlet之前做点什么
    chain.doFilter(request, response); // invoke the rest of the application
    // 在后面Filter和Servlet响应之后做点什么
}

由于 Filter 只影响后面 Filter 和 Servlet,因此每个 Filter 被调用的顺序非常重要。

DelegatingFilterProxy

Spring 提供了一个名为 DelegatingFilterProxy 的 Filter 实现,允许在 Servlet 容器的生命周期和 Spring 的 ApplicationContext 之间建立桥接。Servlet 容器允许使用自己的标准注册过滤器,但它不知道 Spring 定义的 Beans。可以通过标准的 Servlet 容器机制注册 DelegatingFilterProxy,但将所有工作委托给一个实现 Filter 的 Spring Bean。
下图描述了 DelegatingFilterProxy 如何适用于 Filters 和 FilterChain 的:
DelegatingFilterProxy
DelegatingFilterProxy 伪代码:

fun doFilter(request: ServletRequest, response: ServletResponse, chain: FilterChain) {
	// 延迟获取注册为Spring Bean的Filter
	val delegate: Filter = getFilterBean(someBeanName)
	// 将工作委派给Spring Bean
	delegate.doFilter(request, response)
}

DelegatingFilterProxy 的另一个好处是,它允许延迟查看 Filter bean 实例。这很重要,因为容器需要在容器启动之前注册 Filter 实例。然而,Spring 通常使用一个 ContextLoaderListener 来加载 Spring Beans,这在 Filter 实例需要注册之前不会执行。

FilterChainProxy

Spring Security 的 Servlet 支持包含在 FilterChainProxy 中。FilterChainProxy 是 Spring Security 提供的一个特殊的 Filter,它允许通过 SecurityFilterChain 委托给许多 Filter 实例。因为 FilterChainProxy 是一个 Bean,所以它通常包装在 DelegatingFilterProxy 中。
FilterChainProxy

SecurityFilterChain

FilterChainProxy 使用 SecurityFilterChain 来确定应该为此请求调用哪个 Spring 安全过滤器。
SecurityFilterChain
SecurityFilterChain中的安全筛选器通常是Bean,但它们注册到FilterChainProxy而不是DelegatingFilterProxy。
FilterChainProxy为直接向Servlet容器或DelegatingFilterProxy注册提供了许多优势。

首先,它为所有Spring Security的Servlet支持提供了一个起点。因此,如果您试图排除Spring Security的Servlet支持的故障,在FilterChainProxy中添加调试点是一个很好的起点。

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

此外,它在确定何时应该调用 SecurityFilterChain 方面提供了更大的灵活性。在 Servlet 容器中,过滤器仅根据 URL 调用。但是,FilterChainProxy 可以利用 RequestMatcher 接口根据 HttpServletRequest 中的任何内容来确定调用。

事实上,可以使用 FilterChainProxy 来确定应该使用哪个 SecurityFilterChain。这允许为应用程序的不同部分提供完全独立的配置。
多重安全过滤链
在上图中,FilterChainProxy 决定应该使用哪个 SecurityFilterChain。只有匹配的第一个 SecurityFilterChain 将被调用。如果请求/api/messages/的 URL,它将首先匹配 securityFilterChain0的/api/** 模式,因此只会调用 SecurityFilterChain0,即使它也匹配 SecurityFilterChainn。如果请求/messages/的 URL,它将不匹配 SecurityFilterChain0的/api/** 模式,因此 FilterChainProxy 将继续尝试每个 SecurityFilterChain。假设没有其他的 SecurityFilterChain 实例匹配,SecurityFilterChainn 将被调用。

注意 SecurityFilterChain0 只配置了三个安全过滤器实例。但是,SecurityFilterChainn 配置了四个安全过滤器。需要注意的是,每个 SecurityFilterChain 可以是唯一的,并且可以隔离配置。实际上,如果应用程序希望 Spring Security 忽略某些请求,SecurityFilterChain 可能具有零安全筛选器。

Security Filters

安全过滤器通过 SecurityFilterChain API 插入到 FilterChainProxy 中。过滤器的顺序很重要。通常不需要知道 Spring Security 的过滤器的顺序。然而,有时候知道顺序是有益的。
以下是 Spring Security Filter 排序的综合列表:

Filter说明
ChannelProcessingFilter
WebAsyncManagerIntegrationFilter将 Security 上下文与 Spring Web 中用于处理异步请求映射的 WebAsyncManager 进行集成
SecurityContextPersistenceFilter在每次请求处理之前将该请求相关的安全上下文信息加载到 SecurityContextHolder 中,然后在该次请求处理完成之后,将 SecurityContextHolder 中关于这次请求的信息存储到一个“仓储”中,然后将 SecurityContextHolder 中的信息清除,例如在Session中维护一个用户的安全信息就是这个过滤器处理的。
HeaderWriterFilter用于将头信息加入响应中
CorsFilter用于处理跨站请求伪造
LogoutFilter用于处理退出登录
OAuth2AuthorizationRequestRedirectFilter
Saml2WebSsoAuthenticationRequestFilter
UsernamePasswordAuthenticationFilter用于处理基于表单的登录请求,从表单中获取用户名和密码。默认情况下处理来自 /login 的请求。从表单中获取用户名和密码时,默认使用的表单 name 值为 username 和 password,这两个值可以通过设置这个过滤器的usernameParameter 和 passwordParameter 两个参数的值进行修改
OpenIDAuthenticationFilter
DefaultLoginPageGeneratingFilter如果没有配置登录页面,那系统初始化时就会配置这个过滤器,并且用于在需要进行登录时生成一个登录表单页面
DefaultLogoutPageGeneratingFilter
DigestAuthenticationFilter
BearerTokenAuthenticationFilter
BasicAuthenticationFilter检测和处理 http basic 认证
RequestCacheAwareFilter用来处理请求的缓存
SecurityContextHolderAwareRequestFilter主要是包装请求对象request
JaasApiIntegrationFilter
RememberMeAuthenticationFilter当用户没有登录而直接访问资源时, 从 cookie 里找出用户的信息, 如果 Spring Security 能够识别出用户提供的remember me cookie, 用户将不必填写用户名和密码, 而是直接登录进入系统,该过滤器默认不开启。
AnonymousAuthenticationFilter检测 SecurityContextHolder 中是否存在 Authentication 对象,如果不存在为其提供一个匿名 Authentication。
OAuth2AuthorizationCodeGrantFilter
SessionManagementFilter管理 session 的过滤器
ExceptionTranslationFilter处理 AccessDeniedException 和 AuthenticationException 异常
FilterSecurityInterceptor可以看做过滤器链的出口
SwitchUserFilter

异常处理

ExceptionTranslationFilter 允许将 AccessDeniedException 和 AuthenticationException 转换为 HTTP 响应。
ExceptionTranslationFilter 作为安全过滤器之一插入到 FilterChainProxy 中。
异常处理

  • ① 首先,ExceptionTranslationFilter 调用 FilterChain.doFilter (request, response) 来调用应用程序的其余部分。
  • ② 如果用户没有经过身份验证,或者它是 AuthenticationException,那么启动身份验证。
    • SecurityContextHolder 被清空了
    • HttpServletRequest 保存在 RequestCache 中。当用户成功进行身份验证时,使用 RequestCache 重播原始请求。
    • AuthenticationEntryPoint 用于从客户机请求凭据。例如,它可能会重定向到一个登录页面,或者发送一个 WWW-Authenticate 标头。
  • ③ 否则,如果它是AccessDeniedException,则访问被拒绝。调用AccessDeniedHandler来处理拒绝访问。
    如果应用程序没有抛出 AccessDeniedException 或 AuthenticationException,那么 ExceptionTranslationFilter 不会做任何事情。

ExceptionTranslationFilter 的伪代码:

try {
	//调用FilterChain.doFilter(request, response)等同于调用应用程序的其余部分。
	// 这意味着,如果应用程序的另一部分(即FilterSecurityInterceptor或方法安全性)抛出了AuthationException或AccessDeniedException,它将在此处被捕获和处理。
	filterChain.doFilter(request, response);
} catch (AccessDeniedException | AuthenticationException ex) {
	if (!authenticated || ex instanceof AuthenticationException) {
		// 如果用户未经过身份验证,或者它是一个身份验证异常,则启动身份验证
		startAuthentication();
	} else {
		// 否则,访问被拒绝
		accessDenied();
	}
}

参考 SpringSecurity 官方文档

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值