Servlet Authentication Architecture
本章节用于描述Spring Security在Servlet认证中的主要构件
SecurityContextHolder
在Spring Security认证模型的中心 就是 SecurityContextHolder,它包含着SecurityContext
SecurityContextHolder是Spring Security保存被认证用户详情的地方。Spring Security不关心SecurityContextHolder是如何填充的,只有它内部有值,这个值就被当做当前认证的用户。
指示一个用户被认证的最简单的方式就是直接设置SecurityContextHolder
SecurityContext context = SecurityContextHolder.createEmptyContext();
Authentication authentication =
new TestingAuthenticationToken("username", "password", "ROLE_USER");
context.setAuthentication(authentication);
SecurityContextHolder.setContext(context);
-
首先我们创建了一个空的
SecurityContext,最好是创建一个新的SecurityContext而不是使用SecurityContextHolder.getContext().setAuthentication(authentication)来防止多线程的竞态 -
我们创建了一个新的
Authentication对象,Spring Security并不关心SecurityContext中设置了哪种类型的Authentication实现。这里我们使用TestingAuthenticationToken,因为它非常简单,在正式软件开发中,比较常用的是UsernamePasswordAuthenticationToken(userDetails, password, authorities) -
最后,我们为
SecurityContextHolder设置了SecurityContext,Spring Security使用这方面的信息来进行授权
通过访问SecurityContextHolder就可以获取已认证的主体的信息
SecurityContext context = SecurityContextHolder.getContext();
Authentication authentication = context.getAuthentication();
String username = authentication.getName();
Object principal = authentication.getPrincipal();
Collection<? extends GrantedAuthority> authorities = authentication.getAuthorities();
默认情况下,SecurityContextHolder使用ThreadLocal来存储这些信息。不用担心该ThreadLocal使用的安全问题,Spring Security的FilterChainProxy保证SecurityContext的清理。
SecurityContext
SecurityContext是从SecurityContextHolder中获取到的,SecurityContext包含一个Authentication对象。
Authentication
Spring Security中的Authentication interface有两个主要目标。
AuthenticationManager使用它来进行认证- 代表当前已认证的用户,你可以从
SecurityContext中获取当前Authentication
Authentication包含以下几项:
principal:标识用户,当使用username/password进行认证时,它通常是UserDetails的实例credentialls:通常是一个password,一般都会在用户认证会后清除掉来防止泄漏authorities:GrantedAuthority实例是用户被授予的high-level 权限,例如 roles和scopes
GrantedAuthority
你可以从Authentication.getAuthorities() 获取GrantedAuthority实例,该方法提供了GrantedAuthority对象的集合,GrantedAuthority就是授予给principal的authority。例如 ROLE,比如ROLE_ADMINISTRATOR 或 ROLE_HR_SUPERVISOR。
AuthenticationManager
AuthenticationManager定义Spring Security如何处理认证。由AuthenticationManager返回的Authentication会被设置到SecurityContextHolder中。最常见的AuthenticationManager实现就是ProviderManager
ProviderManager
ProviderManager将认证转交给AuthenticationProvider 的List。每个AuthenticationProvider都可以使认证成功,失败或者只是说自己不能对其进行认证 让下游的AuthenticationProvider来进行认证。如果没有AuthenticationProvider能够进行认证,则抛出ProviderNotFoundException。
每个AuthenticationProvider知道如何处理特定类型的认证。例如,一个可能能够验证username/password,另一个可能能够认证一个SAML。
ProviderManager还可以配置一个可选的parent AuthenticationManager,当没有AuthenticationProvider可以处理认证时,该parent会进行认证。parent可以是任何类型的AuthenticationManager,但它经常是ProviderManager的实例。
实际上,多个ProviderManager实例坑由同一个AuthenticationManager。这种情况,在应用有多个SecurityFilterChain 实例 但它们有一些相同的认证逻辑的时候比较常见。
默认地,ProviderManager会在认证成功后,清除掉敏感的credentials 、防止password类似的信息在HttpSession中停留太久。你可以通过设置ProviderManager的eraseCredentialsAfterAuthentication来禁用掉该特性。
AuthenticationProvider
你可以注入多个AuthenticationProvider](https://docs.spring.io/spring-security/site/docs/6.3.0/api/org/springframework/security/authentication/AuthenticationProvider.html) 到[ProviderManager](https://docs.spring.io/spring-security/reference/servlet/authentication/architecture.html#servlet-authentication-providermanager),来执行不同类型的认证,例如, [DaoAuthenticationProvider`支持基于username/password的认证,JwtAuthenticationProvider支持JWT token的认证。
Request Credentials with AuthenticationEntryPoint
AuthenticationEntryPoint用来向client发送HTTP response 来请求认证信息。
有些时候,一些client在请求中就已经携带了认证信息(如 username和password)。在这些情形中,Spring Security不必要提供一个HTTP响应来要求client提供认证信息。对于其他情形,client发送了未认证的request,AuthenticationEntryPoint用来向client索要认证信息。AuthenticationEntryPoint的实现类可能会重定向到登录页,返回一个 WWW-Authenticate header,或者执行其他动作。
AbstractAuthenticationProcessingFilter
AbstractAuthenticationProcessingFilter 是用来认证用户 认证信息的基类 Filter。AbstractAuthenticationProcessingFilter可以对任何认证认证请求进行认证。