docs: update readme guideline

pull/100/head
yanglbme 4 years ago
parent 091a094deb
commit f1802196a4

@ -14,9 +14,11 @@
本项目主要用于记录框架及中间件源码的阅读经验、个人理解及解析,希望能够使阅读源码变成一件简单有趣,且有价值的事情,抽空更新中... (如果本项目对您有帮助,请 watch、star、fork 素质三连一波,鼓励一下作者,谢谢) 本项目主要用于记录框架及中间件源码的阅读经验、个人理解及解析,希望能够使阅读源码变成一件简单有趣,且有价值的事情,抽空更新中... (如果本项目对您有帮助,请 watch、star、fork 素质三连一波,鼓励一下作者,谢谢)
- Netlify: https://schunter.netlify.app - Netlify: https://schunter.netlify.app
- Gitee Pages: https://doocs.gitee.io/source-code-hunter - ~~Gitee Pages: https://doocs.gitee.io/source-code-hunter~~
- GitHub Pages: https://doocs.github.io/source-code-hunter - GitHub Pages: https://doocs.github.io/source-code-hunter
注:😶 Gitee Pages 站点遭 Gitee 官方误判为“包含违禁违规内容”,惨遭下线。
## Spring 系列 ## Spring 系列
### IoC 容器 ### IoC 容器

@ -51,9 +51,10 @@ public class BrowserConfig extends WebSecurityConfigurerAdapter {
这时候启动系统,访问<strong><i>http://localhost:8080/hello</strong></i>,会看到页面已经被重定向到了<strong><i>http://localhost:8080/login.html</strong></i> 这时候启动系统,访问<strong><i>http://localhost:8080/hello</strong></i>,会看到页面已经被重定向到了<strong><i>http://localhost:8080/login.html</strong></i>
![img](../../images/SpringSecurity/d6bd19a2-08d3-4ba6-921c-5b5f57370a16.jpg) ![img](../../images/SpringSecurity/d6bd19a2-08d3-4ba6-921c-5b5f57370a16.jpg)
## 配置用户信息的获取逻辑 ## 配置用户信息的获取逻辑
Spring Security默认会为我们生成一个用户名为user密码随机的用户实例当然我们也可以定义自己用户信息的获取逻辑只需要实现Spring Security提供的***UserDetailService***接口即可,该接口只有一个抽象方法***loadUserByUsername***,具体实现如下: Spring Security 默认会为我们生成一个用户名为 user密码随机的用户实例当然我们也可以定义自己用户信息的获取逻辑只需要实现 Spring Security 提供的**_UserDetailService_**接口即可,该接口只有一个抽象方法**_loadUserByUsername_**,具体实现如下:
``` ```
@Service @Service
@ -203,7 +204,7 @@ public class UserDetailService implements UserDetailsService {
![img](../../images/SpringSecurity/ccd16b38-d724-4c03-949e-3b6ba03268a9.png) ![img](../../images/SpringSecurity/ccd16b38-d724-4c03-949e-3b6ba03268a9.png)
<strong><i>addResourceHandlers()</strong></i>中,会为<strong><i>registry</strong></i>添加两个资源处理器,当请求路径是“/webjars/”时会在”classpath:/META-INF/resources/webjars/“路径下寻找对应的资源,当请求路径是“/**”时会在”classpath:/META-INF/resources/“、”classpath:/resources/“、”classpath:/static/“、”classpath:/public/“路径下寻找对应的资源。 <strong><i>addResourceHandlers()</strong></i>中,会为<strong><i>registry</strong></i>添加两个资源处理器,当请求路径是“/webjars/”时会在”classpath:/META-INF/resources/webjars/“路径下寻找对应的资源,当请求路径是“/\*\*”时会在”classpath:/META-INF/resources/“、”classpath:/resources/“、”classpath:/static/“、”classpath:/public/“路径下寻找对应的资源。
现在我们通过访问<strong><i>http://localhost:8080/login.html</strong></i>来验证这个过程。 现在我们通过访问<strong><i>http://localhost:8080/login.html</strong></i>来验证这个过程。
@ -247,70 +248,69 @@ public class UserDetailService implements UserDetailsService {
我们定义的用户信息的获取逻辑是如何被 Spring Security 应用的呢?让我们通过阅读源码来了解一下。 我们定义的用户信息的获取逻辑是如何被 Spring Security 应用的呢?让我们通过阅读源码来了解一下。
还记得前面我们讲***BrowserConfig配置***被加载的过程吗?***UserDetailService***也是在这个过程中被一起加载完成的,回到**BrowserConfig配置解析**的第一幅图中,如下: 还记得前面我们讲**_BrowserConfig 配置_**被加载的过程吗?**_UserDetailService_**也是在这个过程中被一起加载完成的,回到**BrowserConfig 配置解析**的第一幅图中,如下:
![img](../../images/SpringSecurity/68490740-e03c-4353-b5fc-ac99c0cf0435.png) ![img](../../images/SpringSecurity/68490740-e03c-4353-b5fc-ac99c0cf0435.png)
在断点处位置,<strong><i>authenticationManager()</strong></i>会返回一个***AuthenticationManager***实例,我们进入<strong><i>authenticationManager()</strong></i>中: 在断点处位置,<strong><i>authenticationManager()</strong></i>会返回一个**_AuthenticationManager_**实例,我们进入<strong><i>authenticationManager()</strong></i>中:
![img](../../images/SpringSecurity/e7a1a684-64db-41d0-a5c0-d9a841d86cc1.png) ![img](../../images/SpringSecurity/e7a1a684-64db-41d0-a5c0-d9a841d86cc1.png)
<strong><i>authenticationManager()</strong></i>中,***AuthenticationManager***转由***AuthenticationConfiguration***中获取,我们进入<strong><i>getAuthenticationManager()</strong></i>中: <strong><i>authenticationManager()</strong></i>中,**_AuthenticationManager_**转由**_AuthenticationConfiguration_**中获取,我们进入<strong><i>getAuthenticationManager()</strong></i>中:
![img](../../images/SpringSecurity/d9ae84ae-d60c-4d9c-a7fd-cddeb1142f95.png) ![img](../../images/SpringSecurity/d9ae84ae-d60c-4d9c-a7fd-cddeb1142f95.png)
程序来到***AuthenticationConfiguration***的<strong><i>getAuthenticationManager()</strong></i>中,***AuthenticationManager***转由***AuthenticationManagerBuilder***中获取,我们进入<strong><i>build()</strong></i>中: 程序来到**_AuthenticationConfiguration_**的<strong><i>getAuthenticationManager()</strong></i>中,**_AuthenticationManager_**转由**_AuthenticationManagerBuilder_**中获取,我们进入<strong><i>build()</strong></i>中:
![img](../../images/SpringSecurity/19f71152-f456-4db7-a1d5-f79aaa37253b.png) ![img](../../images/SpringSecurity/19f71152-f456-4db7-a1d5-f79aaa37253b.png)
程序来到***AbstractConfiguredSecurityBuilder***的<strong><i>doBuild()</strong></i>中,这里在构建***AuthenticationManager***实例时需要初始化3个配置类我们重点关注第3个配置类***org.springframework.security.config.annotation.authentication.configuration.InitializeUserDetailsBeanManagerConfigurer***,这个配置类是在***AuthenticationConfiguration***中引入的: 程序来到**_AbstractConfiguredSecurityBuilder_**的<strong><i>doBuild()</strong></i>中,这里在构建**_AuthenticationManager_**实例时,需要初始化 3 个配置类,我们重点关注第 3 个配置类:**_org.springframework.security.config.annotation.authentication.configuration.InitializeUserDetailsBeanManagerConfigurer_**,这个配置类是在**_AuthenticationConfiguration_**中引入的:
![img](../../images/SpringSecurity/9f06c823-645d-413b-8bb6-1d81b8f329ea.png) ![img](../../images/SpringSecurity/9f06c823-645d-413b-8bb6-1d81b8f329ea.png)
我们来到***InitializeUserDetailsBeanManagerConfigurer***的<strong><i>init()</strong></i>中: 我们来到**_InitializeUserDetailsBeanManagerConfigurer_**的<strong><i>init()</strong></i>中:
![img](../../images/SpringSecurity/cd7d6cb9-c6e7-4570-aab8-309adcb15e16.png) ![img](../../images/SpringSecurity/cd7d6cb9-c6e7-4570-aab8-309adcb15e16.png)
这里会新建一个***InitializeUserDetailsManagerConfigurer***实例添加到***AuthenticationManagerBuilder***中。我们回到<strong><i>doBuild()</strong></i>中: 这里会新建一个**_InitializeUserDetailsManagerConfigurer_**实例添加到**_AuthenticationManagerBuilder_**中。我们回到<strong><i>doBuild()</strong></i>中:
![img](../../images/SpringSecurity/3ea76980-417d-4c0c-9330-e0bb241c6a47.png) ![img](../../images/SpringSecurity/3ea76980-417d-4c0c-9330-e0bb241c6a47.png)
可以看到配置类变成了5个其中就有刚刚新建的***InitializeUserDetailsManagerConfigurer***,程序接下来会调用各个配置类的<strong><i>configure()</strong></i>进行配置,我们来到***InitializeUserDetailsManagerConfigurer***的<strong><i>configure()</strong></i>中: 可以看到配置类变成了 5 个,其中就有刚刚新建的**_InitializeUserDetailsManagerConfigurer_**,程序接下来会调用各个配置类的<strong><i>configure()</strong></i>进行配置,我们来到**_InitializeUserDetailsManagerConfigurer_**的<strong><i>configure()</strong></i>中:
![img](../../images/SpringSecurity/ec39a9f6-c97d-4b7d-8843-d20358c1d194.png) ![img](../../images/SpringSecurity/ec39a9f6-c97d-4b7d-8843-d20358c1d194.png)
可以看到在<strong><i>configure()</strong></i>中,就会去bean工厂中寻找***UserDetailsService***类型的bean若是我们没有自定义***UserDetailsService***的实现类的话Spring Security默认会生成一个***InMemoryUserDetailsManager***的实例: 可以看到在<strong><i>configure()</strong></i>中,就会去 bean 工厂中寻找**_UserDetailsService_**类型的 bean若是我们没有自定义**_UserDetailsService_**的实现类的话Spring Security 默认会生成一个**_InMemoryUserDetailsManager_**的实例:
![img](../../images/SpringSecurity/c6a7370c-4afb-4c5d-aa35-ba9c3406b1ed.png) ![img](../../images/SpringSecurity/c6a7370c-4afb-4c5d-aa35-ba9c3406b1ed.png)
***InMemoryUserDetailsManager***是在***UserDetailsServiceAutoConfiguration***类中配置的: **_InMemoryUserDetailsManager_**是在**_UserDetailsServiceAutoConfiguration_**类中配置的:
![img](../../images/SpringSecurity/476f8954-abe3-4e26-bfe1-8c5b4abbf0e0.png) ![img](../../images/SpringSecurity/476f8954-abe3-4e26-bfe1-8c5b4abbf0e0.png)
解决完***UserDetailsService***的加载问题现在我们来看看Spring Security是如何通过***UserDetailsService***获取用户信息的。 解决完**_UserDetailsService_**的加载问题,现在我们来看看 Spring Security 是如何通过**_UserDetailsService_**获取用户信息的。
通过**Spring Boot中开启Spring Security**一节的学习我们知道,登录判断的逻辑是在***UsernamePasswordAuthenticationFilter***中进行的,因此我们在***UsernamePasswordAuthenticationFilter***的<strong><i>attemptAuthenticatio()</strong></i>中打上断点,然后启动项目,访问登录页,输入用户名和密码点击登录后,程序来到***UsernamePasswordAuthenticationFilter***中: 通过**Spring Boot 中开启 Spring Security**一节的学习我们知道,登录判断的逻辑是在**_UsernamePasswordAuthenticationFilter_**中进行的,因此我们在**_UsernamePasswordAuthenticationFilter_**的<strong><i>attemptAuthenticatio()</strong></i>中打上断点,然后启动项目,访问登录页,输入用户名和密码点击登录后,程序来到**_UsernamePasswordAuthenticationFilter_**中:
![img](../../images/SpringSecurity/1282014b-fc29-4c2b-9316-9fdd638653c9.png) ![img](../../images/SpringSecurity/1282014b-fc29-4c2b-9316-9fdd638653c9.png)
这里将验证的逻辑交由***AuthenticationManager***进行,我们进入<strong><i>authenticate()</strong></i>中: 这里将验证的逻辑交由**_AuthenticationManager_**进行,我们进入<strong><i>authenticate()</strong></i>中:
![img](../../images/SpringSecurity/5d511c93-3614-40e0-b3c9-9673c573d60f.png) ![img](../../images/SpringSecurity/5d511c93-3614-40e0-b3c9-9673c573d60f.png)
程序来到***ProviderManager***的<strong><i>authenticate()</strong></i>中,这里将验证的逻辑委托给其父类进行,再次点击进入<strong><i>authenticate()</strong></i>中: 程序来到**_ProviderManager_**的<strong><i>authenticate()</strong></i>中,这里将验证的逻辑委托给其父类进行,再次点击进入<strong><i>authenticate()</strong></i>中:
![img](../../images/SpringSecurity/8dddd63e-c567-4b41-a9d9-8ef8aa6f2a92.png) ![img](../../images/SpringSecurity/8dddd63e-c567-4b41-a9d9-8ef8aa6f2a92.png)
这里将验证的逻辑交由***AuthenticationProvider***进行,我们进入<strong><i>authenticate()</strong></i>中: 这里将验证的逻辑交由**_AuthenticationProvider_**进行,我们进入<strong><i>authenticate()</strong></i>中:
![img](../../images/SpringSecurity/ec796a9b-7c65-49a2-9f9f-7685af7bd57b.png) ![img](../../images/SpringSecurity/ec796a9b-7c65-49a2-9f9f-7685af7bd57b.png)
程序来到***AbstractUserDetailsAuthenticationProvider***的<strong><i>authenticate()</strong></i>中,这里会根据用户名去寻找对应的用户实例,我们进入<strong><i>retrieveUser()</strong></i>中: 程序来到**_AbstractUserDetailsAuthenticationProvider_**的<strong><i>authenticate()</strong></i>中,这里会根据用户名去寻找对应的用户实例,我们进入<strong><i>retrieveUser()</strong></i>中:
![img](../../images/SpringSecurity/3980e264-c073-456a-b808-715edd85633a.png) ![img](../../images/SpringSecurity/3980e264-c073-456a-b808-715edd85633a.png)
程序来到***DaoAuthenticationProvider***的<strong><i>retrieveUser()</strong></i>中,可以看到正是在这里,会从***UserDetailsService***的<strong><i>loadUserByUsername()</strong></i>中寻找对应的用户信息。 程序来到**_DaoAuthenticationProvider_**的<strong><i>retrieveUser()</strong></i>中,可以看到正是在这里,会从**_UserDetailsService_**的<strong><i>loadUserByUsername()</strong></i>中寻找对应的用户信息。
## 参考 ## 参考

@ -2,11 +2,11 @@
Spring Security 是一款基于 Spring 的安全框架,主要包含认证和授权两大安全模块,和另外一款流行的安全框架 Apache Shiro 相比它拥有更为强大的功能。Spring Security 也可以轻松的自定义扩展以满足各种需求,并且对常见的 Web 安全攻击提供了防护支持。如果你的 Web 框架选择的是 Spring那么在安全方面 Spring Security 会是一个不错的选择。 Spring Security 是一款基于 Spring 的安全框架,主要包含认证和授权两大安全模块,和另外一款流行的安全框架 Apache Shiro 相比它拥有更为强大的功能。Spring Security 也可以轻松的自定义扩展以满足各种需求,并且对常见的 Web 安全攻击提供了防护支持。如果你的 Web 框架选择的是 Spring那么在安全方面 Spring Security 会是一个不错的选择。
这里我们使用Spring Boot来集成Spring SecuritySpring Boot版本为***2.5.3***Spring Security版本为***5.5.1***。 这里我们使用 Spring Boot 来集成 Spring SecuritySpring Boot 版本为**_2.5.3_**Spring Security 版本为**_5.5.1_**。
## 开启 Spring Security ## 开启 Spring Security
使用IDEA创建一个Spring Boot项目然后引入***spring-boot-starter-security*** 使用 IDEA 创建一个 Spring Boot 项目,然后引入**_spring-boot-starter-security_**
```java ```java
dependencies { dependencies {
@ -20,7 +20,7 @@ dependencies {
} }
``` ```
接下来我们创建一个***HelloController***,对外提供一个<strong><i>/hello</i></strong>服务: 接下来我们创建一个**_HelloController_**,对外提供一个<strong><i>/hello</i></strong>服务:
```java ```java
@RestController @RestController
@ -60,81 +60,80 @@ Spring Security默认为我们开启了一个简单的安全配置下面让
下面我们通过 debug 来验证这个过程: 下面我们通过 debug 来验证这个过程:
首先,通过前面可以知道,当有请求来到时,最先由***DelegatingFilterProxy***负责接收,因此在***DelegatingFilterProxy***的<strong><i>doFilter()</i></strong>的首行打上断点: 首先,通过前面可以知道,当有请求来到时,最先由**_DelegatingFilterProxy_**负责接收,因此在**_DelegatingFilterProxy_**的<strong><i>doFilter()</i></strong>的首行打上断点:
![image-20210811091719470](../../images/SpringSecurity/image-20210811091719470.png) ![image-20210811091719470](../../images/SpringSecurity/image-20210811091719470.png)
接着***DelegatingFilterProxy***会将请求委派给***FilterChainProxy***进行处理,在***FilterChainProxy***的首行打上断点: 接着**_DelegatingFilterProxy_**会将请求委派给**_FilterChainProxy_**进行处理,在**_FilterChainProxy_**的首行打上断点:
![img](../../images/SpringSecurity/56ac5128-eab7-4b92-912f-ff50bac68a4f.png) ![img](../../images/SpringSecurity/56ac5128-eab7-4b92-912f-ff50bac68a4f.png)
***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>首行打上断点:
![image-20210811091755498](../../images/SpringSecurity/image-20210811091755498.png) ![image-20210811091755498](../../images/SpringSecurity/image-20210811091755498.png)
接下来***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>的首行打上断点:
![image-20210811091815473](../../images/SpringSecurity/image-20210811091815473.png) ![image-20210811091815473](../../images/SpringSecurity/image-20210811091815473.png)
准备完毕后,我们启动项目,然后访问<strong><i>http://localhost:8080/hello</i></strong>,程序首先跳转到***DelegatingFilterProxy***的断点上: 准备完毕后,我们启动项目,然后访问<strong><i>http://localhost:8080/hello</i></strong>,程序首先跳转到**_DelegatingFilterProxy_**的断点上:
![image-20210811091833065](../../images/SpringSecurity/image-20210811091833065.png) ![image-20210811091833065](../../images/SpringSecurity/image-20210811091833065.png)
此时***delegate***还是null的接下来依次执行代码可以看到***delegate***最终被赋值一个***FilterChainProxy***的实例: 此时**_delegate_**还是 null 的,接下来依次执行代码,可以看到**_delegate_**最终被赋值一个**_FilterChainProxy_**的实例:
![img](../../images/SpringSecurity/f045b025-bd97-4222-8a02-51634be6745b.png) ![img](../../images/SpringSecurity/f045b025-bd97-4222-8a02-51634be6745b.png)
接下来程序依次跳转到***FilterChainProxy***的<strong><i>doFilter()</i></strong>和***VirtualFilterChain***的<strong><i>doFilter()</i></strong>中: 接下来程序依次跳转到**_FilterChainProxy_**的<strong><i>doFilter()</i></strong>和**_VirtualFilterChain_**的<strong><i>doFilter()</i></strong>中:
![img](../../images/SpringSecurity/90d3e369-510f-45cb-982d-241d2eedb55c.png) ![img](../../images/SpringSecurity/90d3e369-510f-45cb-982d-241d2eedb55c.png)
![image-20210811092048784](../../images/SpringSecurity/image-20210811092048784.png) ![image-20210811092048784](../../images/SpringSecurity/image-20210811092048784.png)
接着程序跳转到***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 请求):
![img](../../images/SpringSecurity/2e5440bc-9488-4213-a030-0d25153bb2ea.png) ![img](../../images/SpringSecurity/2e5440bc-9488-4213-a030-0d25153bb2ea.png)
接着程序跳转到***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>
![img](../../images/SpringSecurity/47a7bca4-d858-4cb1-b126-347805b74053.png) ![img](../../images/SpringSecurity/47a7bca4-d858-4cb1-b126-347805b74053.png)
接着程序跳转到***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_**的实例:
![img](../../images/SpringSecurity/6b1aded6-5229-47ba-b192-78a7c2622b8c.png) ![img](../../images/SpringSecurity/6b1aded6-5229-47ba-b192-78a7c2622b8c.png)
接着程序跳转到***ExceptionTranslationFilter***的<strong><i>doFilter()</i></strong>中,***ExceptionTranslationFilter***负责处理***FilterSecurityInterceptor***抛出的异常我们在catch代码块的首行打上断点: 接着程序跳转到**_ExceptionTranslationFilter_**的<strong><i>doFilter()</i></strong>中,**_ExceptionTranslationFilter_**负责处理**_FilterSecurityInterceptor_**抛出的异常,我们在 catch 代码块的首行打上断点:
**![img](../../images/SpringSecurity/8efa0b1c-2b32-4d5b-9655-985374326e10.png)** **![img](../../images/SpringSecurity/8efa0b1c-2b32-4d5b-9655-985374326e10.png)**
接着程序跳转到***FilterSecurityInterceptor***的<strong><i>doFilter()</i></strong>中,依次执行代码后程序停留在其父类(***AbstractSecurityInterceptor***)的<strong><i>attemptAuthorization()</i></strong>中: 接着程序跳转到**_FilterSecurityInterceptor_**的<strong><i>doFilter()</i></strong>中,依次执行代码后程序停留在其父类(**_AbstractSecurityInterceptor_**)的<strong><i>attemptAuthorization()</i></strong>中:
![img](../../images/SpringSecurity/d6e99143-6207-43a5-8d04-f0c81baa11b4.png) ![img](../../images/SpringSecurity/d6e99143-6207-43a5-8d04-f0c81baa11b4.png)
***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>中:
![img](../../images/SpringSecurity/6724647c-34ee-4a57-8cfa-b46f57400d14.png) ![img](../../images/SpringSecurity/6724647c-34ee-4a57-8cfa-b46f57400d14.png)
从上图可以看出,决策的关键在<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>中:
![img](../../images/SpringSecurity/4beaa02f-a93d-4d95-9ad1-0d7213cb0e46.png) ![img](../../images/SpringSecurity/4beaa02f-a93d-4d95-9ad1-0d7213cb0e46.png)
<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 块中:
![img](../../images/SpringSecurity/8e1ac9db-5987-484d-abf4-4c6535c60cc6.png) ![img](../../images/SpringSecurity/8e1ac9db-5987-484d-abf4-4c6535c60cc6.png)
接着程序会依次进入***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>
![img](../../images/SpringSecurity/1b03bdd4-6773-4b39-a664-fdf65d104403.png) ![img](../../images/SpringSecurity/1b03bdd4-6773-4b39-a664-fdf65d104403.png)
后续对<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/) 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) 2. [Spring Boot 中开启 Spring Security](https://mrbird.cc/Spring-Boot&Spring-Security.html)

Loading…
Cancel
Save