@ -2,11 +2,11 @@
Spring Security 是一款基于 Spring 的安全框架,主要包含认证和授权两大安全模块,和另外一款流行的安全框架 Apache Shiro 相比, 它拥有更为强大的功能。Spring Security 也可以轻松的自定义扩展以满足各种需求,并且对常见的 Web 安全攻击提供了防护支持。如果你的 Web 框架选择的是 Spring, 那么在安全方面 Spring Security 会是一个不错的选择。
这里我们使用Spring Boot来集成Spring Security, Spring Boot版本为***2.5.3***, Spring Security版本为***5.5.1* **。
这里我们使用 Spring Boot 来集成 Spring Security, Spring Boot 版本为**_2.5.3_**, Spring Security 版本为**_5.5.1_ **。
## 开启 Spring Security
使用IDEA创建一个Spring Boot项目, 然后引入***spring-boot-starter-security* **:
使用 IDEA 创建一个 Spring Boot 项目,然后引入**_spring-boot-starter-security_ **:
```java
dependencies {
@ -20,7 +20,7 @@ dependencies {
}
```
接下来我们创建一个***HelloController* **,对外提供一个< strong > < i > /hello< / i > < / strong > 服务:
接下来我们创建一个**_HelloController_ **,对外提供一个< strong > < i > /hello< / i > < / strong > 服务:
```java
@RestController
@ -60,81 +60,80 @@ Spring Security默认为我们开启了一个简单的安全配置, 下面让
下面我们通过 debug 来验证这个过程:
首先,通过前面可以知道,当有请求来到时,最先由***DelegatingFilterProxy***负责接收,因此在***DelegatingFilterProxy* **的< strong > < i > doFilter()< / i > < / strong > 的首行打上断点:
首先,通过前面可以知道,当有请求来到时,最先由**_DelegatingFilterProxy_**负责接收,因此在**_DelegatingFilterProxy_ **的< strong > < i > doFilter()< / i > < / strong > 的首行打上断点:

接着***DelegatingFilterProxy***会将请求委派给***FilterChainProxy***进行处理,在***FilterChainProxy* **的首行打上断点:
接着**_DelegatingFilterProxy_**会将请求委派给**_FilterChainProxy_**进行处理,在**_FilterChainProxy_ **的首行打上断点:

***FilterChainProxy* **会在< strong > < i > doFilterInternal()< / i > < / strong > 中生成一个内部类***VirtualFilterChain***的实例, 以此来调用Spring Security的整条过滤器链, 在***VirtualFilterChain* **的< strong > < i > doFilter()< / i > < / strong > 首行打上断点:
**_FilterChainProxy_ **会在< strong > < i > doFilterInternal()< / i > < / strong > 中生成一个内部类**_VirtualFilterChain_**的实例,以此来调用 Spring Security 的整条过滤器链,在**_VirtualFilterChain_ **的< strong > < i > doFilter()< / i > < / strong > 首行打上断点:

接下来***VirtualFilterChain***会通过***currentPosition***依次调用存在***additionalFilters***中的过滤器,其中比较重要的几个过滤器有:***UsernamePasswordAuthenticationFilter***、***DefaultLoginPageGeneratingFilter***、***AnonymousAuthenticationFilter***、***ExceptionTranslationFilter***、***FilterSecurityInterceptor* **,我们依次在这些过滤器的< strong > < i > doFilter()< / i > < / strong > 的首行打上断点:
接下来**_VirtualFilterChain_**会通过**_currentPosition_**依次调用存在**_additionalFilters_**中的过滤器,其中比较重要的几个过滤器有:**_UsernamePasswordAuthenticationFilter_**、**_DefaultLoginPageGeneratingFilter_**、**_AnonymousAuthenticationFilter_**、**_ExceptionTranslationFilter_**、**_FilterSecurityInterceptor_ **,我们依次在这些过滤器的< strong > < i > doFilter()< / i > < / strong > 的首行打上断点:

准备完毕后,我们启动项目,然后访问< strong > < i > http://localhost:8080/hello< / i > < / strong > ,程序首先跳转到***DelegatingFilterProxy* **的断点上:
准备完毕后,我们启动项目,然后访问< strong > < i > http://localhost:8080/hello< / i > < / strong > ,程序首先跳转到**_DelegatingFilterProxy_ **的断点上:

此时***delegate***还是null的, 接下来依次执行代码, 可以看到***delegate***最终被赋值一个***FilterChainProxy* **的实例:
此时**_delegate_**还是 null 的,接下来依次执行代码,可以看到**_delegate_**最终被赋值一个**_FilterChainProxy_ **的实例:

接下来程序依次跳转到***FilterChainProxy* **的< strong > < i > doFilter()< / i > < / strong > 和***VirtualFilterChain* **的< strong > < i > doFilter()< / i > < / strong > 中:
接下来程序依次跳转到**_FilterChainProxy_ **的< strong > < i > doFilter()< / i > < / strong > 和**_VirtualFilterChain_ **的< strong > < i > doFilter()< / i > < / strong > 中:


接着程序跳转到***AbstractAuthenticationProcessingFilter***( ***UsernamePasswordAuthenticationFilter* **的父类)的< strong > < i > doFilter()< / i > < / strong > 中,通过< strong > < i > requiresAuthentication()< / i > < / strong > 判定为false( 是否是POST请求) :
接着程序跳转到**_AbstractAuthenticationProcessingFilter_**( **_UsernamePasswordAuthenticationFilter_ **的父类)的< strong > < i > doFilter()< / i > < / strong > 中,通过< strong > < i > requiresAuthentication()< / i > < / strong > 判定为 false( 是否是 POST 请求):

接着程序跳转到***DefaultLoginPageGeneratingFilter* **的< strong > < i > doFilter()< / i > < / strong > 中,通过< strong > < i > isLoginUrlRequest()< / i > < / strong > 判定为false( 请求路径是否是< strong > < i > /login< / i > < / strong > ) :
接着程序跳转到**_DefaultLoginPageGeneratingFilter_ **的< strong > < i > doFilter()< / i > < / strong > 中,通过< strong > < i > isLoginUrlRequest()< / i > < / strong > 判定为 false( 请求路径是否是< strong > < i > /login< / i > < / strong > ) :

接着程序跳转到***AnonymousAuthenticationFilter* **的< strong > < i > doFilter()< / i > < / strong > 中,由于是首次请求,此时< strong > < i > SecurityContextHolder.getContext().getAuthentication()< / i > < / strong > 为null, 因此会生成一个***AnonymousAuthenticationToken* **的实例:
接着程序跳转到**_AnonymousAuthenticationFilter_ **的< strong > < i > doFilter()< / i > < / strong > 中,由于是首次请求,此时< strong > < i > SecurityContextHolder.getContext().getAuthentication()< / i > < / strong > 为 null, 因此会生成一个**_AnonymousAuthenticationToken_ **的实例:

接着程序跳转到***ExceptionTranslationFilter* **的< strong > < i > doFilter()< / i > < / strong > 中,***ExceptionTranslationFilter***负责处理***FilterSecurityInterceptor***抛出的异常, 我们在catch 代码块的首行打上断点:
接着程序跳转到**_ExceptionTranslationFilter_ **的< strong > < i > doFilter()< / i > < / strong > 中,**_ExceptionTranslationFilter_**负责处理**_FilterSecurityInterceptor_**抛出的异常,我们在 catch 代码块的首行打上断点:
****
接着程序跳转到***FilterSecurityInterceptor* **的< strong > < i > doFilter()< / i > < / strong > 中,依次执行代码后程序停留在其父类(***AbstractSecurityInterceptor* **)的< strong > < i > attemptAuthorization()< / i > < / strong > 中:
接着程序跳转到**_FilterSecurityInterceptor_ **的< strong > < i > doFilter()< / i > < / strong > 中,依次执行代码后程序停留在其父类(**_AbstractSecurityInterceptor_ **)的< strong > < i > attemptAuthorization()< / i > < / strong > 中:

***accessDecisionManager***是***AccessDecisionManager***(访问决策器)的实例,***AccessDecisionManager***主要有3个实现类: ***AffirmativeBased***(一票通过), **ConsensusBased**(少数服从多数)、UnanimousBased(一票否决),此时***AccessDecisionManager***的的实现类是***AffirmativeBased***,我们可以看到程序进入***AffirmativeBased* **的< strong > < i > decide()< / i > < / strong > 中:
**_accessDecisionManager_**是**_AccessDecisionManager_**(访问决策器)的实例,**_AccessDecisionManager_**主要有 3 个实现类:**_AffirmativeBased_**(一票通过), **ConsensusBased**(少数服从多数)、UnanimousBased(一票否决),此时**_AccessDecisionManager_**的的实现类是**_AffirmativeBased_**,我们可以看到程序进入**_AffirmativeBased_ **的< strong > < i > decide()< / i > < / strong > 中:

从上图可以看出,决策的关键在< strong > < i > voter.vote(authentication, object, configAttributes)< / i > < / strong > 这句代码上,通过跟踪调试,程序最终进入***AuthenticationTrustResolverImpl* **的< strong > < i > isAnonymous()< / i > < / strong > 中:
从上图可以看出,决策的关键在< strong > < i > voter.vote(authentication, object, configAttributes)< / i > < / strong > 这句代码上,通过跟踪调试,程序最终进入**_AuthenticationTrustResolverImpl_ **的< strong > < i > isAnonymous()< / i > < / strong > 中:

< strong > < i > isAssignableFrom()< / i > < / strong > 判断前者是否是后者的父类,而***anonymousClass***被固定为***AnonymousAuthenticationToken.class***,参数***authentication***由前面***AnonymousAuthenticationFilter***可以知道是***AnonymousAuthenticationToken* **的实例,因此< strong > < i > isAnonymous()< / i > < / strong > 返回true, ***FilterSecurityInterceptor***抛出***AccessDeniedException***异常,程序返回***ExceptionTranslationFilter***的catch 块中:
< strong > < i > isAssignableFrom()< / i > < / strong > 判断前者是否是后者的父类,而**_anonymousClass_**被固定为**_AnonymousAuthenticationToken.class_**,参数**_authentication_**由前面**_AnonymousAuthenticationFilter_**可以知道是**_AnonymousAuthenticationToken_ **的实例,因此< strong > < i > isAnonymous()< / i > < / strong > 返回 true, **_FilterSecurityInterceptor_**抛出**_AccessDeniedException_**异常,程序返回**_ExceptionTranslationFilter_**的 catch 块中:

接着程序会依次进入***DelegatingAuthenticationEntryPoint***、***LoginUrlAuthenticationEntryPoint***中,最后由***LoginUrlAuthenticationEntryPoint* **的< strong > < i > commence()< / i > < / strong > 决定重定向到< strong > < i > /login< / i > < / strong > :
接着程序会依次进入**_DelegatingAuthenticationEntryPoint_**、**_LoginUrlAuthenticationEntryPoint_**中,最后由**_LoginUrlAuthenticationEntryPoint_ **的< strong > < i > commence()< / i > < / strong > 决定重定向到< strong > < i > /login< / i > < / strong > :

后续对< strong > < i > /login< / i > < / strong > 的请求同样会经过之前的执行流程,在***DefaultLoginPageGeneratingFilter* **的< strong > < i > doFilter()< / i > < / strong > 中,通过< strong > < i > isLoginUrlRequest()< / i > < / strong > 判定为true( 请求路径是否是< strong > < i > /login< / i > < / strong > ) ,直接返回***login.html* **,也就是我们开头看到的登录页面。
后续对< strong > < i > /login< / i > < / strong > 的请求同样会经过之前的执行流程,在**_DefaultLoginPageGeneratingFilter_ **的< strong > < i > doFilter()< / i > < / strong > 中,通过< strong > < i > isLoginUrlRequest()< / i > < / strong > 判定为 true( 请求路径是否是< strong > < i > /login< / i > < / strong > ) ,直接返回**_login.html_ **,也就是我们开头看到的登录页面。
当我们输入用户名和密码,点击***Sign in***,程序来到***AbstractAuthenticationProcessingFilter* **的< strong > < i > doFilter()< / i > < / strong > 中,通过< strong > < i > requiresAuthentication()< / i > < / strong > 判定为true( 是否是POST请求) ,因此交给其子类***UsernamePasswordAuthenticationFilter***进行处理,***UsernamePasswordAuthenticationFilter***会将用户名和密码封装成一个***UsernamePasswordAuthenticationToken* **的实例并进行校验,当校验通过后会将请求重定向到我们一开始请求的路径:< strong > < i > /hello< / i > < / strong > 。
当我们输入用户名和密码,点击**_Sign in_**,程序来到**_AbstractAuthenticationProcessingFilter_ **的< strong > < i > doFilter()< / i > < / strong > 中,通过< strong > < i > requiresAuthentication()< / i > < / strong > 判定为 true( 是否是 POST 请求),因此交给其子类**_UsernamePasswordAuthenticationFilter_**进行处理,**_UsernamePasswordAuthenticationFilter_**会将用户名和密码封装成一个**_UsernamePasswordAuthenticationToken_ **的实例并进行校验,当校验通过后会将请求重定向到我们一开始请求的路径:< strong > < i > /hello< / i > < / strong > 。
后续对< strong > < i > /hello< / i > < / strong > 的请求经过过滤器链时就可以一路开绿灯直到最终交由***HelloController* **返回"Hello World"。
后续对< strong > < i > /hello< / i > < / strong > 的请求经过过滤器链时就可以一路开绿灯直到最终交由**_HelloController_ **返回"Hello World"。
## 参考
1. [Spring Security Reference ](https://docs.spring.io/spring-security/site/docs/current/reference/html5/ )
2. [Spring Boot 中开启 Spring Security ](https://mrbird.cc/Spring-Boot&Spring-Security.html )