From c30c88dfbd83c47e1661c220fe949e39f468c4a5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BE=90=E6=B6=9B?= Date: Thu, 29 Jul 2021 22:03:08 +0800 Subject: [PATCH] =?UTF-8?q?post:=E5=AE=8C=E6=88=90Spring=20Security?= =?UTF-8?q?=E5=9F=BA=E7=A1=80=E6=96=87=E7=AB=A0=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...pring-secrutiy-authentication-manager.puml | 130 ++++++++++++++++ drawings/spring-security-cotnextholder.puml | 90 +++++++++++ ...ng-security-daoauthneticationprovider.puml | 71 +++++++++ drawings/spring-security-servlet-flow.puml | 34 +++++ drawings/spring-security-webflux-flow.puml | 26 ++++ source/_posts/spring-security-basic.md | 140 ++++++++++++++++++ 6 files changed, 491 insertions(+) create mode 100644 drawings/spring-secrutiy-authentication-manager.puml create mode 100644 drawings/spring-security-cotnextholder.puml create mode 100644 drawings/spring-security-daoauthneticationprovider.puml create mode 100644 drawings/spring-security-servlet-flow.puml create mode 100644 drawings/spring-security-webflux-flow.puml create mode 100644 source/_posts/spring-security-basic.md diff --git a/drawings/spring-secrutiy-authentication-manager.puml b/drawings/spring-secrutiy-authentication-manager.puml new file mode 100644 index 0000000..7e2d4db --- /dev/null +++ b/drawings/spring-secrutiy-authentication-manager.puml @@ -0,0 +1,130 @@ +@startuml +skinparam Shadowing false +skinparam class { + BackgroundColor White +} +hide empty members + +interface AuthenticationManager { + + Authentication authenticate(Authentication authentication) +} + +class ProviderManager { + - AuthenticationEventPublisher eventPublisher + - List providers + - MessageSourceAccessor messages + - AuthenticationManager parent + - boolean eraseCredentialsAfterAuthentication + + ProviderManager(AuthenticationProvider... providers) + + ProviderManager(List providers) + + ProviderManager(List providers, AuthenticationManager parent) + + void afterPropertiesSet() + - void checkState() + - void copyDetails(Authentication source, Authentication dest) + + List getProviders() + + void setMessageSource(MessageSource messageSource) + + void setAuthenticationEventPublisher(AuthenticationEventPublisher eventPublisher) + + void setEraeseCredentialsAfterAuthentication(boolean eraseSecretData) + + boolean isEraseCredentialsAfterAuthentication() +} + +class NullEventPublisher + +interface AuthenticationEventPublisher { + + void publishAuthenticationFailure(AuthenticationException exception, Authentication authentication) + + void publishAuthenticationSuccess(Authentication authentication) +} + +interface AuthenticationProvider { + + Authentication authenticate(Authentication authentication) + + boolean supports(Class authentication) +} + +abstract AbstractUserDetailsAuthenticationProvider { + # MessageSourceAccessor messages + - UserCache userCache + - boolean forcePrincipalAsString + - boolean hideUserNotFoundException + - UserDetailsChecker preAuthenticationChecks + - UserDetailsChecker postAuthenticationChecks + - GrantedAuthoritiesMapper authoritiesMapper + #{abstract} void additionalAuthenticationChecks(UserDetails userDetails, UsernamePasswordAuthenticationToken authentication) + + void afterPropertiesSet() + - String determineUsername(Authentication authentication) + # Authentication createSuccessAuthentication(Object principal, Authentication authentication, UserDetails user) + # void doAfterPropertiesSet() + #{abstract} UserDetails retrieveUser(String username, UsernamePasswordAuthenticationToken authentication) +} + +interface UserDetailsChecker { + + void check(UserDetails toCheck) +} + +class DefaultPreAuthenticationChecks +class DefaultPostAuthenticationChecks + +interface UserCache { + + UserDetails getUserFromCache(String username) + + void putUserInCache(UserDetails user) + + void removeUserFromCache(String username) +} + +class UserDetails { + + Collection getAuthorities() + + String getPassword() + + String getUsername() + + boolean isAccountNonExpired() + + boolean isAccountNonLocked() + + boolean isCredentialsNonExpired() + + boolean isEnabled() +} + +interface GrantedAuthoritiesMapper { + + Collection mapAuthorities(Collection authorities) +} + +interface GrantedAuthority { + + String getAuthority() +} + +class SimpleGrantedAuthority { + - String role +} + +class SpringCacheBasedUserCache +class UsernamePasswordAuthenticationToken { + - Object principal + - Object credentials +} +abstract AbstractAuthenticationToken { + - Collection authorities + - Object details + - boolean authenticated +} +interface Authentication +interface CredentialsContainer { + + void eraseCredentials() +} + +AuthenticationManager <|.. ProviderManager +ProviderManager +-- NullEventPublisher +NullEventPublisher ..|> AuthenticationEventPublisher +AuthenticationProvider "1..*" --* "1" ProviderManager +AbstractUserDetailsAuthenticationProvider ..|> AuthenticationProvider +DefaultPreAuthenticationChecks --+ AbstractUserDetailsAuthenticationProvider +DefaultPostAuthenticationChecks --+ AbstractUserDetailsAuthenticationProvider +UserDetailsChecker <|.. DefaultPreAuthenticationChecks +UserDetailsChecker <|.. DefaultPostAuthenticationChecks +UserCache --* AbstractUserDetailsAuthenticationProvider +UserDetails --* AbstractUserDetailsAuthenticationProvider +UserDetails --* UserDetailsChecker +UserDetails --* UserCache +AbstractUserDetailsAuthenticationProvider *--- GrantedAuthoritiesMapper +GrantedAuthoritiesMapper *-- GrantedAuthority +GrantedAuthority <|.. SimpleGrantedAuthority +UserCache <|.. SpringCacheBasedUserCache +UsernamePasswordAuthenticationToken --* AbstractUserDetailsAuthenticationProvider +AbstractAuthenticationToken <|-- UsernamePasswordAuthenticationToken +Authentication <|.. AbstractAuthenticationToken +CredentialsContainer <|.. AbstractAuthenticationToken +@enduml \ No newline at end of file diff --git a/drawings/spring-security-cotnextholder.puml b/drawings/spring-security-cotnextholder.puml new file mode 100644 index 0000000..85ab49c --- /dev/null +++ b/drawings/spring-security-cotnextholder.puml @@ -0,0 +1,90 @@ +@startuml +skinparam Shadowing false +skinparam class { + BackgroundColor White +} +hide empty members + +package org.springframework.security.core { + package context { + class SecurityContextHolder { + -{static} SecurityContextHolderStrategy strategy + -{static} int initilizeCount + -{static} void initilize() + +{static} void clearContext() + +{static} SecurityContext getContext() + +{static} int getInitilizeCount() + +{static} void setContext(SecurityContext context) + +{static} void setStrategyName(String strategyName) + +{static} SecurityContextHolderStrategy getContextHolderStrategy() + +{static} SecurityContext createEmptyContext() + } + + interface SecurityContextHolderStrategy { + + void clearContext() + + SecurityContext getContext() + + void setContext(SecurityContext context) + + SecurityContext createEmptyContext() + } + + class ThreadLocalSecurityContextHolderStrategy { + -{static} ThreadLocal contextHolder + } + + class GlobalSecurityContextHolderStrategy { + -{static} SecurityContext contextHolder + } + + class InheritableThreadLocalSecurityContextHolderStrategy { + -{static} ThreadLocal contextHolder + } + + class ReactiveSecurityContextHolder { + +{static} Mono getContext() + -{static} boolean hasSecurityContext(Context context) + -{static} Mono getSecurityContext(Context context) + +{static} Function clearContext() + +{static} Context withSecurityContext(Mono securityContext) + +{static} Context withAuthentication(Authentication authntication) + } + + class SecurityContext { + + Authentication getAuthentication() + + void setAuthentication(Authentication authentication) + } + } + + interface Authentication { + + Collection getAuthorities() + + Object getCredentials() + + Object getDetails() + + Object getPrincipal() + + boolean isAuthticated() + + setAuthenticated(boolean isAuthenticated) + } + + interface GrantedAuthority { + + String getAuthority() + } +} + +package java.security { + interface Principal { + + String getName() + + boolean implies(Subject subject) + } +} + +SecurityContextHolderStrategy --* SecurityContextHolder +ThreadLocalSecurityContextHolderStrategy ..|> SecurityContextHolderStrategy +GlobalSecurityContextHolderStrategy ..|> SecurityContextHolderStrategy +InheritableThreadLocalSecurityContextHolderStrategy ...|> SecurityContextHolderStrategy +SecurityContext --* SecurityContextHolderStrategy +SecurityContext -* ReactiveSecurityContextHolder +SecurityContext --* ThreadLocalSecurityContextHolderStrategy +SecurityContext --* InheritableThreadLocalSecurityContextHolderStrategy +SecurityContext --* GlobalSecurityContextHolderStrategy +Authentication --* SecurityContext +GrantedAuthority -* Authentication +Authentication -|> Principal +@enduml \ No newline at end of file diff --git a/drawings/spring-security-daoauthneticationprovider.puml b/drawings/spring-security-daoauthneticationprovider.puml new file mode 100644 index 0000000..59796bf --- /dev/null +++ b/drawings/spring-security-daoauthneticationprovider.puml @@ -0,0 +1,71 @@ +@startuml +skinparam Shadowing false +skinparam class { + BackgroundColor White +} +hide empty members + +abstract AbstractUserDetailsAuthenticationProvider +class UserDetails + +class DaoAuthenticationProvider { + - PasswordEncoder passwordEncoder + - String userNotFoundEncodedPassword + - UserDetailsService userDetailsService + - UserDetailsPasswordService userDetailsPasswordService + - void prepareTimingAttackProtection() + - void mitigateAgainstTimingAttack(UsernamePasswordAuthenticationToken authentication) +} + +interface UserDetailsService { + + UserDetails loadUserByUsername(String username) +} + +interface UserDetailsPasswordService { + + UserDetails update(UserDetails user, String password) +} + +interface PasswordEncoder { + + String encode(CharSequence rawPassword) + + boolean matches(CharSequence rawPassword, String encodedPassword) + + boolean upgradeEncoding(String rawPassword) +} +class UsernamePasswordAuthenticationToken + +abstract AbstractPasswordEncoder { + # byte[] encode(CharSequence rawPassword, byte[] salt) + # byte[] encodeAndConcatenate(CharSequence rawPassword, byte[] salt) + # {static} boolean matches(byte[] expected, byte[] actual) +} + +class DelegatingPasswordEncoder { + - {static} String PREFIX + - {static} String SUFFIX + - String idForEncode + - PasswordEncoder passwordEncoderForEncode + - Map idToPasswordEncoder + - PasswordEncoder defaultPasswordEncoderForMatches + + DelegatingPasswordEncoder(String idForEncode, Map idToPasswordEncoder) + + void setDefaultPasswordEncoderForMatches(PasswordEncoder defaultPasswordEncoderForMatches) + - String extractId(String prefixEncodedPassword) + - String extractEncodedPassword(String prefixEncodedPassword) +} + +class UnmappedIdPasswordEncoder +class BCryptPasswordEncoder +class Pbkdf2PasswordEncoder + +DaoAuthenticationProvider --|> AbstractUserDetailsAuthenticationProvider +UserDetailsService --* DaoAuthenticationProvider +UserDetailsPasswordService --* DaoAuthenticationProvider +UsernamePasswordAuthenticationToken --* DaoAuthenticationProvider +PasswordEncoder ---* DaoAuthenticationProvider +UserDetails --* UserDetailsService +UserDetails --* UserDetailsPasswordService +AbstractPasswordEncoder ..|> PasswordEncoder +DelegatingPasswordEncoder ..|> PasswordEncoder +DelegatingPasswordEncoder +-- UnmappedIdPasswordEncoder +UnmappedIdPasswordEncoder ..|> PasswordEncoder +BCryptPasswordEncoder ..|> PasswordEncoder +Pbkdf2PasswordEncoder ..|> PasswordEncoder +@enduml \ No newline at end of file diff --git a/drawings/spring-security-servlet-flow.puml b/drawings/spring-security-servlet-flow.puml new file mode 100644 index 0000000..d7306cc --- /dev/null +++ b/drawings/spring-security-servlet-flow.puml @@ -0,0 +1,34 @@ +@startdot spring-security-servlet-flow +digraph servlet { + rankdir=TB + edge [color="#A60738"] + node [color="#A60738"] + + client [shape=box, label="客户端"] + servlet [shape=box, label="Servlet"] + tokenStorage [shape=box, label="令牌缓存"] + + { rank=same; tokenStorage; RememberMeServices } + + client -> FilterChain [label="原始请求"] + FilterChain -> SecurityFilterChain [label="经过中转的请求"] + SecurityFilterChain -> UsernamePasswordAuthenticationProviderFilter [label="携带有用户名和密码的请求"] + UsernamePasswordAuthenticationProviderFilter -> AuthenticationManager [label="UsernamePasswordAuthenticationToken"] + AuthenticationManager -> SecurityContext [label="认证后的Authentication"] + SecurityFilterChain -> BearerTokenAuthenticationFilter [label="携带有Authorization头的请求"] + BearerTokenAuthenticationFilter -> AuthenticationManager [label="BearerTokenAuthenticationToken"] + AuthenticationManager -> DaoAuthenticationProvider [label="UsernamePasswordAuthenticationToken"] + DaoAuthenticationProvider -> UserDetailsService [label="UserDetails"] + UserDetailsService -> DaoAuthenticationProvider [label="认证后的UserDetails"] + DaoAuthenticationProvider -> PasswordEncoder [label="用户认证密码"] + PasswordEncoder -> DaoAuthenticationProvider [label="经过加密的密码"] + DaoAuthenticationProvider -> SecurityContext [label="认证后的Authentication"] + SecurityFilterChain -> RememberMeAuthenticationFilter [label="携带RememberMe认证的请求"] + RememberMeAuthenticationFilter -> RememberMeServices [label="RememberMeAuthenticationToken"] + RememberMeServices -> tokenStorage [label="认证令牌信息"] + RememberMeServices -> SecurityContext [label="认证后的Authentication"] + SecurityFilterChain -> AnonymousAuthenticationFilter [label="不携带任何认证信息请求"] + AnonymousAuthenticationFilter -> SecurityContext [label="匿名Authentication"] + SecurityContext -> servlet [label="已经加入认证信息的请求"] +} +@enddot \ No newline at end of file diff --git a/drawings/spring-security-webflux-flow.puml b/drawings/spring-security-webflux-flow.puml new file mode 100644 index 0000000..2e24b5a --- /dev/null +++ b/drawings/spring-security-webflux-flow.puml @@ -0,0 +1,26 @@ +@startdot spring-security-webflux-flow +digraph webfluxFlow { + rankdir=TB + edge [color="#A60738"] + node [color="#A60738"] + + client [shape=box, label="客户端"] + endpoint [shape=box, label="处理端点"] + ' tokenStorage [shape=box, label="令牌缓存"] + + client -> SecurityFilterChain [label="HTTP请求"] + SecurityFilterChain -> AnonymousAuthenticationWebFilter [label="ServerWebExchange"] + AnonymousAuthenticationWebFilter -> SecurityContext [label="匿名Authentication"] + SecurityContext -> endpoint + SecurityFilterChain -> AuthenticationWebFilter [label="ServerWebExchange"] + AuthenticationWebFilter -> ReactivePreAuthenticatedAuthenticationManager [label="认证前的Authentication"] + ReactivePreAuthenticatedAuthenticationManager -> ReactiveUserDetailsService [label="用户认证信息"] + ReactiveUserDetailsService -> ReactivePreAuthenticatedAuthenticationManager [label="PreAuthenticatedAuthenticationToken"] + ReactivePreAuthenticatedAuthenticationManager -> AuthenticationWebFilter [label="PreAuthenticatedAuthenticationToken"] + AuthenticationWebFilter -> SecurityContext [label="认证后的Authentication"] + AuthenticationWebFilter -> JwtReactiveAuthenticationManager [label="认证前的Authentication"] + JwtReactiveAuthenticationManager -> AuthenticationWebFilter [label="BearerTokenAuthenticationToken"] + JwtReactiveAuthenticationManager -> JwtAuthenticationConverter [label="Jwt"] + JwtAuthenticationConverter -> JwtReactiveAuthenticationManager [label="JwtAuthenticationToken"] +} +@enddot \ No newline at end of file diff --git a/source/_posts/spring-security-basic.md b/source/_posts/spring-security-basic.md new file mode 100644 index 0000000..7eab5e6 --- /dev/null +++ b/source/_posts/spring-security-basic.md @@ -0,0 +1,140 @@ +--- +title: Spring Security基础 +tags: + - Java + - Spring + - Spring Security + - 安全认证 +categories: + - - JVM + - Spring +keywords: 'Spring,Spring Security,UserDetails,SecurityContext' +date: 2021-07-29 21:59:41 +--- + + +Spring Security 是 Spring 框架中的安全框架,整个 Spring Security 在学习起来都是比较复杂的。这也是因为 Spring Security 在设计上所需要的组件比较多,所以比较难以理解。本文将尝试通过组成 Spring Securit 功能的常用功能组件以图的形式进行说明,以方便对于 Spring Security 能有一个比较清楚直观的理解。 + +本篇关于 Spring Security 的文章主要由三部分组成,分别是: + +1. {% post_link spring-security-basic %} +1. {% post_link spring-security-webmvc %} +1. {% post_link spring-security-webflux %} + +## 基础组成 + +### SecurityContextHolder + +在 Spring Security 中,`SecurityContextHolder`是 Spring Security 记录认证结果的核心。所有已经被认证过的用户都将被保存在`SecurityContextHolder`中。每个`SecurityContextHolder`实例中会利用一个`SecurityContextHolderStrategy`实例来保存已经完成验证的`SecurityContext`实例。 + +所以,在 Spring Security 中,每一个非空的`SecurityContextHolder`就代表着一个已经被认证过的用户,而且在 Spring Security 中,非空的`SecurityContextHolder`实例也常常被用来表示当前已经被认证过的用户。 + +以下是`SecurityContextHolder`系列接口的构成示意图。 + +{% oss_image spring-security/spring-security-cotnextholder.svg "SecurityContextHolder类结构图" %} + +从这张图上可以看出,`SecurityContextHolder`中主要是利用其中的`SecurityContextHolderStrategy`实例来管理其中持有的`SecurityContext`实例。但是在实际使用时,我们并不需要直接调用`SecurityContextHolderStrategy`实例,只需要利用`SecurityContextHolder`来完成操作即可。 + +!!! note "" + 如果想要在`SecurityContextHolder`中使用不同的策略实例,可以在配置文件中设定`spring.security.strategy`的值,这一配置项可以取以下三个值:`MODE_THREADLOCAL`、`MODE_INHERITABLETHREADLOCAL`、`MODE_GLOBAL`。在没有设置所需要使用的策略时,会默认采用`MODE_THREADLOCAL`。 + +### SpringContext + +`SpringContext`类就比较简单了,根据上图描述,`SpringContext`里就是包含了一个`Authentication`接口实例。 + +### Authentication + +`Authentication`接口是Spring Security中的认证功能核心,在Spring Security中主要执行以下两个功能: + +1. 作为`AuthenticationManager`的输入数据来对用户进行认证。 +1. 代表一个目前已经被认证的用户,在这种情况下,`Authentication`实例可以从`SecurityContext`中获得。 + +`Authentication`中通常会包含三种内容:Principal、Credentials、Authorities。虽然在上图中,Principal和Credentials都是`Object`类型的,但Principal通常都是以`UserDetails`接口实例的形式出现,而Credentials通常都是字符串。Authorities在上图中表示是一组`GrantedAuthority`实例,但是大部分情况下都是采用`SimpleGrantedAuthority`类包装的字符串组成Authorites集合。 + +### AuthenticationManager + +`AuthenticationManager`在Spring Security中定义了如何执行用户身份验证,经过`AuthenticationManager`的执行以后,Spring Security即可以向`SecurityContextHolder`中写入被返回的验证信息。 + +!!! note "" + `AuthenticationManager`通常会被应用成一个Servlet Filter,而这也是Spring Web MVC基于Servlet的处理流程。 + +由于`AuthenticationManager`只是一个接口,所以在实际使用时通常都使用`ProviderManager`类来实现其所定义的功能。常用的基于`AuthenticationManager`接口的类结构图如下。 + +{% oss_image spring-security/spring-secrutiy-authentication-manager.svg "AuthenticationManager类结构图" %} + +从这张图上可以看出来,平时在使用`ProviderManager`的时候,实际上是在构建认证所需要的`AuthenticationProvider`。Spring Security提供了多个实现了`AuthenticationProvider`接口的抽象类,例如`AbstractUserDetailsAuthenticationProvider`,这些抽象类中实现了很多常用的功能,如果需要实现一个自定义`AuthenticationProvider`,可以直接继承这些抽象类。Spring Security中也是基于这些抽象类提供了许多常见的认证场景支持。这些常用的认证类主要有以下几个。 + +1. `DaoAuthenticationProvider`,提供基于用户名和密码的认证。 +1. `JwtAuthenticationProvider`,提供基于JWT的认证。 +1. `AnonymousAuthenticationProvider`,提供基于`AnonymousAuthenticationToken`的匿名用户认证。 +1. `JaasAuthenticationProvider`,提供基于Java认证与授权的认证。 +1. `RemoteAuthenticationProvider`,提供远程认证。 +1. `LdapAuthenticationProvider`,提供基于LDAP的认证。 +1. `OAuth2AuthenticationCodeAuthenticationProvider`,提供基于授权码的OAuth 2.0认证。 +1. `OAuth2LoginAuthenticationProvider`,提供基于登录的OAuth 2.0认证。 +1. `OpenIDAuthenticationProvider`,提供基于OpenID授权的认证。 +1. `PreAuthenticatedAuthenticationProvider`,提供基于预认证的授权认证。 +1. `CasAuthenticationProvider`,提供基于JA-SIG中央认证服务的授权认证。 +1. `OpaqueTokenAuthenticationProvider`,提供基于不透明令牌的授权认证。 +1. `JwtAuthenticationProvider`,提供基于JWT的授权认证。 + +### DaoAuthenticationProvider + +`DaoAuthenticationProvider`是Spring Security中最常用的Authentication Provider,因为大部分认证场景都是基于用户名和密码的。`DaoAuthenticationProvider`进行认证功能的核心就是一个`UserDetailsService`实例,通过`UserDetailsService`可以完成用户提供的用户名和密码的验证,并且可以返回一个可供`Authentication`保存用户信息的`UserDetails`。 + +以下是`DaoAuthenticationProvider`的类结构图,在构建基于用户名和密码的认证过程中参考使用。 + +{% oss_image spring-security/spring-security-daoauthneticationprovider.svg DaoAuthenticationProvider类结构图 %} + +### AbstractAuthenticationProcessingFilter + +`AbstractAuthenticationProcessingFilter`继承了`GenericFilterBean`,是Spring Servlet应用中Security相关过滤器定义的抽象类。基于Servlet的Web应用中,一个请求在到达负责响应的Controller Bean之前,需要经过数层过滤器的处理。`AbstractAuthenticationProcessingFilter`所执行的功能就是把Spring Security的认证与授权功能以过滤器的形式插入到Web请求的处理流程之中。 + +!!! note "" + 在Spring Security的官方文档中,对于过滤器的使用还提到了`DelegatingFilterProxy`和`FilterChainProxy`,这两个代理类主要是用于在Servlet处理流程中插入Filter组合的,在绝大多数情况下都不需要手动控制。在使用过程中一般只需要定义所需要使用的Filter即可。 + +对于`SecurityFilterChain`在整个Servlet请求处理流程中插入的Filter的先后顺序,Spring Security官方文档是给了一个[列表](https://docs.spring.io/spring-security/site/docs/current/reference/html5/#servlet-security-filters)的,可以直接参考使用。我们在日常使用过程中所需要关注的Filter主要有以下几个(按调用顺序排列,不一定全都使用)。 + +1. `CorsFilter` +1. `CsrfFilter` +1. `LogoutFilter` +1. `OAuth2AuthorizationRequestRedirectFilter` +1. `OAuth2LoginAuthenticationFilter` +1. `UsernamePasswordAuthenticationFilter` +1. `OpenIDAuthenticationFilter` +1. `BearerTokenAuthenticationFilter` +1. `RememberMeAuthenticationFilter` +1. `AnonymousAuthenticationFilter` +1. `OAuth2AuthorizationCodeGrantFilter` +1. `SessionManagementFilter` +1. `ExceptionTranslationFilter` + +#### UsernamePasswordAuthenticationFilter + +以`UsernamePasswordAuthenticationFilter`为例,它会将请求传入的用户名和密码整合放入`UsernamePasswordAuthenticationToken`中,这是一个实现了`Authentication`接口的类,主要用途就是存放用户认证信息。之后,`UsernamePasswordAuthenticaitonToken`实例就会被放入`AuthenticationManager`实例中进行认证。 + +所以在认证过程中所获取的`Authentication`接口实例,一般都是`UsernamePasswordAuthenticationToken`类的实例。 + +#### RememberMeAuthenticationFilter + +`RememberMeAuthenticationFilter`可以主要用来提供已经完成认证的用户自动登录功能的过滤器,通过定义一个`RememberMeServices`接口或者`AbstractRememberMeServices`抽象类的实例,可以利用其中的`loginSuccess()`方法生成一个针对于已经完成认证的用户的记录,这种记录可以是Token、Cookie或者其他任何形式。所生成的用户记录可以在下一次请求的`autoLogin()`方法中使用,并在请求中恢复认证记录。 + +#### BearerTokenAuthenticationFilter + +`BearerTokenAuthenticaitonFilter`的主要功能就是解析HTTP请求头中的`Authorization`,并将其中所携带的Token内容解析出来,形成一个`BearerTokenAuthenticationToken`实例,然后放入`AuthenticationManager`中进行认证,最后将认证结果放入`SecurityContextHolder`中。这里出现的`BearerTokenAuthenticationToken`也是一个`AbstractAuthenticationToken`抽象类的子类,其中也包含了认证所需要使用的用户名、认证资料等。 + +## Spring Web MVC中的认证流程 + +Spring Web MVC中主要是依赖于Servlet Filter组成的管道来对HTTP请求中携带的认证消息进行处理。下面借用一个伪数据流图来简单示意一下在一次Web请求中认证信息的处理过程。 + +{% oss_image spring-security/spring-security-servlet-flow.svg "Spring Web MVC认证流程示意" %} + +从这个图上可以看出来,在Spring Web MVC中,这些实现了`javax.servlet.Filter`的认证过滤器才是才是真正启动认证过程执行的起点。而我们在使用时大多只需要为它们提供它们所依赖的工作实例即可,例如`UserDetailsService`、`PasswordEncoder`等。 + +## Spring WebFlux中的认证流程 + +Spring WebFlux中的依赖的是WebFilter组成的过滤器管道来对HTTP请求中携带的认证消息进行处理。这里同样借用一个伪数据流图来进行简单的示意。 + +{% oss_image spring-security/spring-security-webflux-flow.svg "Spring WebFlux认证流程示意" %} + +Spring WebFlux中的认证流程和参与组件要比Spring Web MVC少很多。从上图可以看出来,启动认证过程的起点依旧是实现了`WebFilter`接口的过滤器,但是不同的认证过滤器开始使用不同的`AuthenticationManager`实例。但是整个流程中不变的是,所有已经完成认证的结果还都将保存到`SecurityContext`实例中。 \ No newline at end of file