阅读spring security文档
This commit is contained in:
@@ -10,7 +10,7 @@
|
||||
- [处理Security异常](#处理security异常)
|
||||
- [在多次认证之间保存request](#在多次认证之间保存request)
|
||||
- [RequestCache](#requestcache)
|
||||
- [Spring Security身份认证的结构](#spring-security身份认证的结构)
|
||||
- [Spring Security身份认证架构](#spring-security身份认证架构)
|
||||
- [SecurityContextHolder](#securitycontextholder)
|
||||
- [SecurityContext](#securitycontext)
|
||||
- [Authentication](#authentication)
|
||||
@@ -20,7 +20,6 @@
|
||||
- [AuthenticationProvider](#authenticationprovider)
|
||||
- [AuthenticationEntryPoint](#authenticationentrypoint)
|
||||
- [AbstractAuthenticationProcessingFilter](#abstractauthenticationprocessingfilter)
|
||||
- [认证流程](#认证流程)
|
||||
- [用户名/密码认证](#用户名密码认证)
|
||||
- [Form Login](#form-login)
|
||||
- [用户被重定向到登录页面的过程](#用户被重定向到登录页面的过程)
|
||||
@@ -204,76 +203,108 @@ SecurityFilterChain springSecurity(HttpSecurity http) throws Exception {
|
||||
}
|
||||
```
|
||||
|
||||
## Spring Security身份认证的结构
|
||||
## Spring Security身份认证架构
|
||||
### SecurityContextHolder
|
||||
Spring Security身份认证的核心模型,SecurityContextHolder包含有SecurityContext。
|
||||
> Spring Security将被认证用户的详细信息(details)存储在SecurityContextHolder中。Spring Security不在乎该SecurityContextHolder是如何被填充的,只要该SecurityContextHolder有值,那么其将会被用作当前已经被认证的用户。
|
||||
SecurityContextHolder用于存储用户的认证信息,是Spring Security身份认证认证模型的核心,SecurityContextHolder中包含有SecurityContextHolder,SecurityContextHolder负责将当前线程和SecurityContext相关联。
|
||||
|
||||
> ***将用户标识为已认证的最简单的方法是为该用户设置SecurityContextHolder。***
|
||||
如果想要将用户表示为已经通过身份认证,最简单的方式是向SecurityContextHolder中设置值,示例如下:
|
||||
|
||||
```java
|
||||
// 设置SecurityContextHolder
|
||||
SecurityContext context = SecurityContextHolder.createEmptyContext();
|
||||
SecurityContext context = SecurityContextHolder.createEmptyContext(); // 1
|
||||
Authentication authentication =
|
||||
new TestingAuthenticationToken("username", "password", "ROLE_USER");
|
||||
new TestingAuthenticationToken("username", "password", "ROLE_USER"); //2
|
||||
context.setAuthentication(authentication);
|
||||
|
||||
SecurityContextHolder.setContext(context);
|
||||
SecurityContextHolder.setContext(context); //3
|
||||
```
|
||||
默认情况下,SecurityContextHolder通过ThreadLocal来存储SecurityContext,故而SecurityContext对于位于同一线程之下的方法来说都可以访问。
|
||||
> 使用ThreadLocal来存储SecurityContext是相当安全的,如果想要在该已认证主体的请求被处理完成之后清除SecurityContext,Spring Security中的FilterChainProxy会保证该SecurityContext被清除。
|
||||
|
||||
上述流程如下所示:
|
||||
1. 创建了一个空的SecurityContext。在最开始,应该通过createEmptyContext方法创建一个空的SecurityContext,而不是使用`getContext().setAuthentication(authentication)`,从而避免在多线程下的竞争场景
|
||||
2. 第二步中,创建了一个Authentication对象,Spring Security不关注向SecurityContext中塞入了哪种类型的Authentication实现
|
||||
3. 在第三步中,将SecurityContext设置到了SecurityContextHolder中,spring使用设置的SecurityContext信息进行身份认证
|
||||
|
||||
如果后续操作想要访问当前已认证用户的信息,可以访问SecurityContextHolder。
|
||||
|
||||
```java
|
||||
SecurityContext context = SecurityContextHolder.getContext();
|
||||
Authentication authentication = context.getAuthentication();
|
||||
String username = authentication.getName();
|
||||
Object principal = authentication.getPrincipal();
|
||||
Collection<? extends GrantedAuthority> authorities = authentication.getAuthorities();
|
||||
```
|
||||
|
||||
在默认情况下,`SecurityContextHolder`使用ThreadLocal来存SecurityContext,故而SecurityContext在同一线程内可以共享。在SecurityContextHolder中使用ThreadLocal是安全的,不会发生当前请求完成而SecurityContext未被清理的情况,Spring的FilterChainProxy保证SecurityContext会被清理。
|
||||
|
||||
在一些场景下,可能不希望使用ThreadLocal来存储ContextHolder,例如在swing程序下,可能希望整个java虚拟机中的线程都共享同一个security context。可以在启动时为SecurityContextHolder指定一个strategy,该strategy决定context应该如何被存储。
|
||||
|
||||
对于应用程序内所有线程共享的security context,可以使用`SecurityContextHolder.MODE_GLOBAL`策略。
|
||||
|
||||
### SecurityContext
|
||||
SecurityContext从SecurityContextHolder中获得,SecurityContext中含有Authentication对象。
|
||||
Security Context用于存储用户的身份认证信息,从SecurityContextHolder中获取。
|
||||
|
||||
### Authentication
|
||||
Authentication在Spring Security中具有两个目的:
|
||||
- 作为AuthenticationManager的输入,用于提供待认证用户的认证凭证。当用于该场景下时,isAuthenticated()方法返回值应该为false
|
||||
- 代表当前已经认证过的用户。当前的Authentication可以从SecurityContext中获取,而默认情况下SecurityContext是存储在ThreadLocal中的
|
||||
Authentication在Spring Security中具有两种使用场景:
|
||||
- 在用户尚未被认证的场景,向`AuthenticationManager`提供用于身份认证的凭证
|
||||
- 在用户已经被认证的场景,Authentication用于代表当前已经通过认证的用户,此时可以通过SecurityContext来获取用户认证信息
|
||||
|
||||
Authentication含有如下部分:
|
||||
Authentication包含有如下信息:
|
||||
- 主体(principal):用于标识用户,当通过username/password进行认证时,其通常是UserDetails类的实例
|
||||
- 凭据(credentials):通常是password,在许多场景下凭据会在用户认证成功之后被清空,为了保证凭据不会被泄露
|
||||
- 权限(authorities):该GrantedAuthority集合是用户被授予的高层次许可。许可通常是用户角色或者作用域范围。
|
||||
- 凭据(credentials):通常是password,在许多场景下为了保证凭证不会被泄露,在用户通过认证后,凭证将会被清空
|
||||
- 权限(authorities):代表用户被授予的权限,为`GrantedAuthority`的实例集合
|
||||
|
||||
### GrantedAuthority
|
||||
GrantedAuthority是用户被授予的高层次许可,譬如用户角色或者作用域范围。
|
||||
GrantedAuthority可以通过Authentication.getAuthorities()方法来获得,该方法会返回一个GrantedAuthentication的集合。每个GrantedAuthentication都是一项被授予该用户的权限。
|
||||
GrantedAuthority是用户被授予的权限。
|
||||
|
||||
可以通过`Authentication.getAuthorities()`方法来获取用户被授予的权限集合,该方法会返回`GrantedAuthority`的集合。
|
||||
|
||||
### AuthenticationManager
|
||||
AuthenticationManager的API定义了Security Filters如何来执行身份认证。对于身份认证返回的Authentication,会被调用AuthenticationManager的controller设置到SecurityContextHolder中。
|
||||
> AuthenticationManager的实现可以是任何类,但是最通用的实现仍然是ProviderManager
|
||||
AuthenticationManager定义了Security Filters如何执行认证。
|
||||
|
||||
AuthenticationManager会对传递给其的Authentication执行认证操作,如果认证成功,会返回一个被完全填充的Authetication对象。
|
||||
|
||||
在AuthenticationManager的authenticate方法返回Authentication对象之后,调用AuthenticationManager的controller会将返回的Authentication对象设置到SecurityContextHolder中。
|
||||
|
||||
AuthenticationManager的实现可以是任何类,最通用的实现类是`ProviderManager`.
|
||||
|
||||
### ProviderManager
|
||||
ProviderManager是AuthenticationManager的最通用实现。ProviderManager将工作委托给一系列AuthenticationProvider。
|
||||
> 对于每个ProviderManager,都可以决定将该认证标识为成功、失败,或者将认证工作委托给下游AuthenticationProvider。
|
||||
> 如果所有的AuthenticationProvider都没有将该认证标识为成功或者失败,那么整个认证流程失败,并且抛出ProviderNotFoundException异常。
|
||||
> ProviderNotFoundException是一个特殊的AuthenticationException,该异常代表对传入Authentication的类型并没有配置该类型的ProviderManager
|
||||
ProviderManager是最常用的AuthenticationManager实现类,ProviderMananger将认证工作委托给了一个`AuthenticationProvider`的实例集合。在集合中的每一个AuthenticationProvider,都可以决定当前认证工作是认证成功、认证失败还是将认证工作交由集合中的后续AuthenticationProvider决定。
|
||||
|
||||
> 在实践中,每个AuthenticationProvider知道如何处理一种特定类型的Authentication
|
||||
如果AuthenticationProvider集合中没有任何AuthenticationProvider可以决定当前认证工作是成功或是失败,那么当前认证工作会失败,并且抛出`ProviderNotFoundException`异常。该异常代表当前的认证方式没有被支持。
|
||||
|
||||
默认情况下,ProviderManager在认证请求成功后会尝试清除返回的Authentication对象中任何敏感的凭据信息,这将会保证password等敏感信息保存时间尽可能地短,减少泄露的风险。
|
||||
> 在实践中,每个AuthenticationProvider都明确其应该负责的认证方式,例如一个AuthenticationProvider负责用户名/密码形式的登录,另一个AuthenticationProvider负责saml形式的登录。故而,可以在支持多种认证方式的情况下,只通过一个AuthenticationManager bean对象来进行整合多种认证方式。
|
||||
|
||||
对于ProviderManager,可以配置一个parent ProviderManager,当本级没有AuthenticationProvider能够完成认证工作时,将会将认证工作委托给父级manager。
|
||||
|
||||
默认情况下,ProviderManager将会在认证成功后,清空任任何成功登录后返回Authenticaiton对象的凭证信息。
|
||||
|
||||
### AuthenticationProvider
|
||||
复数个AuthenticationProvider可以被注入到ProviderManager中,每个AuthenticationProvider可以负责一种专门类型的认证,例如DaoAuthenticationProvider负责username/password认证,JwtAuthenticationProvider负责jwt token的认证。
|
||||
AuthenticationProvider负责执行特定类型的认证工作,可以将多个AuthenticationProvider对象注入到同一个ProviderMananger中。
|
||||
|
||||
例如,DaoAuthenticationProvider支持基于用户名/密码的认证,JwtAuthenticationManager则是支持基于jwt形式的认证。
|
||||
|
||||
### AuthenticationEntryPoint
|
||||
AuthenticationEntryPoint用来发送一个Http Response,用来向客户端请求认证凭据。
|
||||
某些情况下,客户端在请求资源时会主动在请求中包含凭据,如username/password等。在这种情况下,服务端并不需要再专门发送Http Response来向客户端请求认证凭据。
|
||||
> AuthenticationEntryPoint用来向客户端请求认证凭据,AuthenticationEntryPoint的实现可能会执行一个从定向操作,将请求重定向到一个登录页面用于获取凭据,并且返回一个WWW-Authentication Header。
|
||||
AuthenticationEntryPoint用于服务端向用户请求凭证(例如,重定向到登录页面,向客户端发送`WWW-Authenticate`响应等。)
|
||||
|
||||
一些场景下,用户在请求资源时会主动发送在请求中包含凭据。在这些场景下,spring security不用向客户返回相应来要求客户提供凭证。
|
||||
|
||||
在其他场景下,客户端可能在未认证的情况下发送请求来访问资源,这时,可以使用`AuthenticationEntryPoint`的实现来向客户请求凭证。`AuthenticationEntryPoint`的实现可以重定向到登录页,或是向客户端发送`WWW-Authenticate`响应,或是采用其他方式来向客户端请求凭据。
|
||||
|
||||
### AbstractAuthenticationProcessingFilter
|
||||
该类作为base filter用来对用户的凭据进行认证。再凭据被认证之前,Spring Security通常会通过AuthenticationEntryPoint向客户端请求认证凭据。
|
||||
之后,AbstractAuthenticationProcessingFilter会对提交的任何认证请求进行认证。
|
||||
#### 认证流程
|
||||
1. 当用户提交认证凭据之后,AbstractAuthenticationProcesingFilter会根据HttpServletRequest对象创建一个Authentication对象用于认证,该Authentication的类型取决于AbstractAuthenticationProcessingFilter的子类类型。例如,UsernamePasswordAuthenticationFilter会通过提交request中的username和password创建UsernamePasswordAuthenticationToken。
|
||||
2. 然后将构建产生的Authentication对象传入到AuthenticationManager中,进行认证
|
||||
3. 如果认证失败,那么会失败,SecurityContextHolder会被清空,RememberMeService.logFail方法将会被调用,AuthenticationFailureHandler也会被调用
|
||||
4. 如果认证成功,那么SessionAuthenticationStrategy将会收到登录的通知
|
||||
5. 该Authentication对象再认证成功之后将会被设置到SecurityContextHolder中
|
||||
6. RemeberMeService.logSuccess将会被调用
|
||||
7. ApplicationEventPublisher发布InteractiveAuthenticationSuccessEvent.
|
||||
8. AuthenticationSuccessHandler被调用
|
||||
`AbstractAuthenticationProcessingFilter`用于认证用户的凭据。在凭据可以被认证之前,spring security通过会通过`AuthentciationEntryPoint`来向客户请求凭据。
|
||||
|
||||
AbstractAuthenticationProcessingFilter通过如下流程来执行认证:
|
||||
1. 当用户提交凭据时,AbstractAuthenticationProcessingFilter会根据将要被认证的request来创建一个Authentication对象,创建Authentication的类型基于AbstractAuthenticationProcessingFilter的继承类。例如,UsernamePasswordAuthenticationFilter将会基于request中提交的username和password创建一个UsernamePasswordAuthenticationToken
|
||||
2. 接下来,Authentication将会被传递给AuthenticationMananger用于认证
|
||||
3. 如果认证失败,那么
|
||||
1. SecurityContextHolder将会被清空
|
||||
2. RememberMeServices.loginFail方法将会被调用,如果remeberMe没有被调用,那么将不会执行任何操作
|
||||
3. AuthenticationFailureHandler将会被调用
|
||||
4. 如果认证成功:
|
||||
1. 将会通知SessionAuthenticaitonStrategy将会被提示有一个新的登录
|
||||
2. Authentication将会被设置到SecurityContextHolder中。如果想要在多个请求之前保存context,需要手动调用`SecurityContextRepository#saveContext`方法,调用后,SecurityContext可以在后续的请求中自动被设置
|
||||
3. 调用RememberMeServices.loginSuccess方法,如果没有设置rememberMe,那么不会执行任何操作
|
||||
4. ApplicationEventPublisher将会发不InteractiveAuthenticationSuccessEvent事件
|
||||
5. AuthenticationSuccessHandler将会被调用
|
||||
|
||||
## 用户名/密码认证
|
||||
### Form Login
|
||||
|
||||
Reference in New Issue
Block a user