mirror of
https://github.com/zhwei820/learn.lianglianglee.com.git
synced 2025-11-16 14:13:47 +08:00
fix img
This commit is contained in:
@@ -159,7 +159,7 @@ function hide_canvas() {
|
||||
<p>过滤器是一种通用机制,在处理 Web 请求的过程中发挥了重要作用。可以说,目前市面上所有的 Web 开发框架都或多或少使用了过滤器完成对请求的处理,Spring Security 也不例外。Spring Security 中的过滤器架构是<strong>基于 Servlet</strong>构建的,所以我们先从 Servlet 中的过滤器开始说起。</p>
|
||||
<h4>Servlet 与管道-过滤器模式</h4>
|
||||
<p>和业界大多数处理 Web 请求的框架一样,Servlet 中采用的最基本的架构就是管道-过滤器(Pipe-Filter)架构模式。管道-过滤器架构模式的示意图如下所示:</p>
|
||||
<p><img src="assets/Cgp9HWDJ2q6ADHioAABYcUjpFY4513.png" alt="Drawing 0.png" /></p>
|
||||
<p><img src="assets/Cgp9HWDJ2q6ADHioAABYcUjpFY4513.png" alt="png" /></p>
|
||||
<p>管道-过滤器架构模式示意图</p>
|
||||
<p>结合上图我们可以看到,处理业务逻辑的组件被称为过滤器,而处理结果通过相邻过滤器之间的管道进行传输,这样就构成了一个过滤器链。</p>
|
||||
<p>在 Servlet 中,代表过滤器的 Filter 接口定义如下:</p>
|
||||
@@ -181,7 +181,7 @@ function hide_canvas() {
|
||||
<p>请注意,<strong>过滤器链中的过滤器是有顺序的</strong>,这点非常重要,我们在本讲后续内容中会针对这点展开讲解。</p>
|
||||
<h4>Spring Security 中的过滤器链</h4>
|
||||
<p>在 Spring Security 中,其核心流程的执行也是依赖于一组过滤器,这些过滤器在框架启动后会自动进行初始化,如图所示:</p>
|
||||
<p><img src="assets/Cgp9HWDJ2rmAfrXzAABUC_CslH8843.png" alt="Drawing 1.png" /></p>
|
||||
<p><img src="assets/Cgp9HWDJ2rmAfrXzAABUC_CslH8843.png" alt="png" /></p>
|
||||
<p>Spring Security 中的过滤器链示意图</p>
|
||||
<p>在上图中,我们看到了几个常见的 Filter,比如 BasicAuthenticationFilter、UsernamePasswordAuthenticationFilter 等,这些类都直接或间接实现了 Servlet 中的 Filter 接口,并完成某一项具体的认证机制。例如,上图中的 BasicAuthenticationFilter 用来验证用户的身份凭证;而 UsernamePasswordAuthenticationFilter 会检查输入的用户名和密码,并根据认证结果决定是否将这一结果传递给下一个过滤器。</p>
|
||||
<p>请注意,<strong>整个 Spring Security 过滤器链的末端是一个 FilterSecurityInterceptor,它本质上也是一个 Filter</strong>。但与其他用于完成认证操作的 Filter 不同,它的核心功能是<strong>实现权限控制</strong>,也就是用来判定该请求是否能够访问目标 HTTP 端点。FilterSecurityInterceptor 对于权限控制的粒度可以到方法级别,能够满足前面提到的精细化访问控制。我们在 06 讲“权限管理:如何剖析 Spring Security 的授权原理?”中已经对这个拦截器做了详细的介绍,这里就不再展开了。</p>
|
||||
@@ -210,7 +210,7 @@ function hide_canvas() {
|
||||
</code></pre>
|
||||
<p>这里我们定义了一个 LoggingFilter,用来记录已经通过用户认证的请求中包含的一个特定的消息头“UniqueRequestId”,通过这个唯一的请求 Id,我们可以对请求进行跟踪、监控和分析。在实现一个自定义的过滤器组件时,我们通常会从 ServletRequest 中获取请求数据,并在 ServletResponse 中设置响应数据,然后通过 filterChain 的 doFilter() 方法将请求继续在过滤器链上进行传递。</p>
|
||||
<p>接下来,我们想象这样一种场景,业务上我们需要根据客户端请求头中是否包含某一个特定的标志位,来决定请求是否有效。如图所示:</p>
|
||||
<p><img src="assets/CioPOWDJ2seAeDCHAABb0EbFFhw357.png" alt="Drawing 2.png" /></p>
|
||||
<p><img src="assets/CioPOWDJ2seAeDCHAABb0EbFFhw357.png" alt="png" /></p>
|
||||
<p>根据标志位设计过滤器示意图</p>
|
||||
<p>这在现实开发过程中也是一种常见的应用场景,可以实现定制化的安全性控制。针对这种应用场景,我们可以实现如下所示的 RequestValidationFilter 过滤器:</p>
|
||||
<pre><code>public class RequestValidationFilter implements Filter {
|
||||
@@ -234,11 +234,11 @@ function hide_canvas() {
|
||||
<p>现在,我们已经实现了几个有价值的过滤器了,下一步就是将这些过滤器整合到 Spring Security 的整个过滤器链中。这里,我想特别强调一点,和 Servlet 中的过滤器一样,<strong>Spring Security 中的过滤器也是有顺序的</strong>。也就是说,将过滤器放置在过滤器链的具体位置需要符合每个过滤器本身的功能特性,不能将这些过滤器随意排列组合。</p>
|
||||
<p>我们来举例说明合理设置过滤器顺序的重要性。在[“用户认证:如何使用 Spring Security 构建用户认证体系?”]一讲中我们提到了 HTTP 基础认证机制,而在 Spring Security 中,实现这一认证机制的就是 BasicAuthenticationFilter。</p>
|
||||
<p>如果我们想要实现定制化的安全性控制策略,就可以实现类似前面介绍的 RequestValidationFilter 这样的过滤器,并放置在 BasicAuthenticationFilter 前。这样,在执行用户认证之前,我们就可以排除掉一批无效请求,效果如下所示:</p>
|
||||
<p><img src="assets/CioPOWDJ2tWAOEqvAABaXzH63gI541.png" alt="Drawing 3.png" /></p>
|
||||
<p><img src="assets/CioPOWDJ2tWAOEqvAABaXzH63gI541.png" alt="png" /></p>
|
||||
<p>RequestValidationFilter 的位置示意图</p>
|
||||
<p>上图中的 RequestValidationFilter 确保那些没有携带有效请求头信息的请求不会执行不必要的用户认证。基于这种场景,把 RequestValidationFilter 放在 BasicAuthenticationFilter 之后就不是很合适了,因为用户已经完成了认证操作。</p>
|
||||
<p>同样,针对前面已经构建的 LoggingFilter,原则上我们可以把它放在过滤器链的任何位置,因为它只记录了日志。但有没有更合适的位置呢?结合 RequestValidationFilter 来看,同样对于一个无效的请求而言,记录日志是没有什么意义的。所以 LoggingFilter 应该放置在 RequestValidationFilter 之后。另一方面,对于日志操作而言,通常只需要记录那些已经通过认证的请求,所以也推荐将 LoggingFilter 放在 BasicAuthenticationFilter 之后。最终,这三个过滤器之间的关系如下图所示:</p>
|
||||
<p><img src="assets/CioPOWDJ2t-AMX8kAAAyTVuPA_s000.png" alt="Drawing 4.png" /></p>
|
||||
<p><img src="assets/CioPOWDJ2t-AMX8kAAAyTVuPA_s000.png" alt="png" /></p>
|
||||
<p>三个过滤器的位置示意图</p>
|
||||
<p>在 Spring Security 中,提供了一组可以往过滤器链中添加过滤器的工具方法,包括 addFilterBefore()、addFilterAfter()、addFilterAt() 以及 addFilter() 等,它们都定义在 HttpSecurity 类中。这些方法的含义都很明确,使用起来也很简单,例如,想要实现如上图所示的效果,我们可以编写这样的代码:</p>
|
||||
<pre><code>@Override
|
||||
@@ -257,7 +257,7 @@ protected void configure(HttpSecurity http) throws Exception {
|
||||
<p>这里,我们使用了 addFilterBefore() 和 addFilterAfter() 方法在 BasicAuthenticationFilter 之前和之后分别添加了 RequestValidationFilter 和 LoggingFilter。</p>
|
||||
<h3>Spring Security 中的过滤器</h3>
|
||||
<p>下表列举了 Spring Security 中常用的过滤器名称、功能以及它们的顺序关系:</p>
|
||||
<p><img src="assets/CioPOWDP_VCAc51eAADyJ6yBpaY941.png" alt="image.png" /></p>
|
||||
<p><img src="assets/CioPOWDP_VCAc51eAADyJ6yBpaY941.png" alt="png" /></p>
|
||||
<p>Spring Security 中的常见过滤器一览表</p>
|
||||
<p>这里以最基础的 UsernamePasswordAuthenticationFilter 为例,该类的定义及核心方法 attemptAuthentication 如下所示:</p>
|
||||
<pre><code>public class UsernamePasswordAuthenticationFilter extends
|
||||
@@ -294,7 +294,7 @@ protected void configure(HttpSecurity http) throws Exception {
|
||||
}
|
||||
</code></pre>
|
||||
<p>围绕上述方法,我们结合前面已经介绍的认证和授权相关实现原理,可以引出该框架中一系列核心类并梳理它们之间的交互结构,如下图所示:</p>
|
||||
<p><img src="assets/CioPOWDP_VuAIAgyAAFOMaUuRl4162.png" alt="image" /></p>
|
||||
<p><img src="assets/CioPOWDP_VuAIAgyAAFOMaUuRl4162.png" alt="png" /></p>
|
||||
<p>UsernamePasswordAuthenticationFilter 相关核心类图</p>
|
||||
<p>上图中的很多类,我们通过名称就能明白它的含义和作用。以位于左下角的 SecurityContextHolder 为例,它是一个典型的 Holder 类,存储了应用的安全上下文对象 SecurityContext,而这个上下文对象中就包含了用户的认证信息。</p>
|
||||
<p>我们也可以大胆猜想,它的内部应该使用 ThreadLocal 确保线程访问的安全性。更具体的,我们已经在“权限管理:如何剖析 Spring Security 的授权原理?”中讲解过 SecurityContext 的使用方法。</p>
|
||||
@@ -302,7 +302,7 @@ protected void configure(HttpSecurity http) throws Exception {
|
||||
<h3>小结与预告</h3>
|
||||
<p>这一讲我们关注于 Spring Security 中的一个核心组件——过滤器。在请求-响应式处理框架中,过滤器发挥着重要的作用,它用来实现对请求的拦截,并定义认证和授权逻辑。同时,我们也可以根据需要实现各种自定义的过滤器组件,从而实现对 Spring Security 的动态扩展。本讲对 Spring Security 中的过滤器架构和开发方式都做了详细的介绍,你可以反复学习。</p>
|
||||
<p>本讲内容总结如下:</p>
|
||||
<p><img src="assets/CioPOWDJ2viAN_UiAAB6xt7iJjo286.png" alt="Drawing 6.png" /></p>
|
||||
<p><img src="assets/CioPOWDJ2viAN_UiAAB6xt7iJjo286.png" alt="png" /></p>
|
||||
<p>最后,给你留一道思考题:在 Spring Security 中,你能简单描述使用过滤器实现用户认证的操作过程吗?欢迎你在留言区和我分享自己的观点。</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user