blog/source/_posts/spring-security-basic.md

138 lines
11 KiB
Markdown
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

---
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 能有一个比较清楚直观的理解。<!-- more -->
本篇关于 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 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 MVC中的认证流程
Spring MVC中主要是依赖于Servlet Filter组成的管道来对HTTP请求中携带的认证消息进行处理。下面借用一个伪数据流图来简单示意一下在一次Web请求中认证信息的处理过程。
{% oss_image spring-security/spring-security-servlet-flow.svg "Spring MVC认证流程示意" %}
从这个图上可以看出来在Spring 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 MVC少很多。从上图可以看出来启动认证过程的起点依旧是实现了`WebFilter`接口的过滤器,但是不同的认证过滤器开始使用不同的`AuthenticationManager`实例。但是整个流程中不变的是,所有已经完成认证的结果还都将保存到`SecurityContext`实例中。