spring security文档阅读
This commit is contained in:
@@ -1,3 +1,48 @@
|
||||
- [Spring Security](#spring-security)
|
||||
- [Spring Security简介](#spring-security简介)
|
||||
- [Spring Security自动配置](#spring-security自动配置)
|
||||
- [Spring Security结构](#spring-security结构)
|
||||
- [DelegatingFilterProxy](#delegatingfilterproxy)
|
||||
- [FilterChainProxy](#filterchainproxy)
|
||||
- [SecurityFilterChain](#securityfilterchain)
|
||||
- [SecurityFilters](#securityfilters)
|
||||
- [添加自定义filter到SecurityFilterChain](#添加自定义filter到securityfilterchain)
|
||||
- [处理Security异常](#处理security异常)
|
||||
- [在多次认证之间保存request](#在多次认证之间保存request)
|
||||
- [RequestCache](#requestcache)
|
||||
- [Spring Security身份认证的结构](#spring-security身份认证的结构)
|
||||
- [SecurityContextHolder](#securitycontextholder)
|
||||
- [SecurityContext](#securitycontext)
|
||||
- [Authentication](#authentication)
|
||||
- [GrantedAuthority](#grantedauthority)
|
||||
- [AuthenticationManager](#authenticationmanager)
|
||||
- [ProviderManager](#providermanager)
|
||||
- [AuthenticationProvider](#authenticationprovider)
|
||||
- [AuthenticationEntryPoint](#authenticationentrypoint)
|
||||
- [AbstractAuthenticationProcessingFilter](#abstractauthenticationprocessingfilter)
|
||||
- [认证流程](#认证流程)
|
||||
- [用户名/密码认证](#用户名密码认证)
|
||||
- [Form Login](#form-login)
|
||||
- [用户被重定向到登录页面的过程](#用户被重定向到登录页面的过程)
|
||||
- [认证用户名和密码过程](#认证用户名和密码过程)
|
||||
- [基本Authentication](#基本authentication)
|
||||
- [基本Authentication的认证流程](#基本authentication的认证流程)
|
||||
- [Digest Authentication(摘要认证,***不安全***)](#digest-authentication摘要认证不安全)
|
||||
- [摘要认证中的随机数](#摘要认证中的随机数)
|
||||
- [密码存储方式](#密码存储方式)
|
||||
- [内存中存储密码](#内存中存储密码)
|
||||
- [内存中存储密码时使用defaultPasswordEncoder](#内存中存储密码时使用defaultpasswordencoder)
|
||||
- [JDBC Authentication](#jdbc-authentication)
|
||||
- [User Schema](#user--schema)
|
||||
- [Group Schema](#group-schema)
|
||||
- [配置Datasource](#配置datasource)
|
||||
- [创建JdbcUserDetailsManager Bean对象](#创建jdbcuserdetailsmanager-bean对象)
|
||||
- [UserDetails](#userdetails)
|
||||
- [UserDetailsService](#userdetailsservice)
|
||||
- [PasswordEncoder](#passwordencoder)
|
||||
- [DaoAuthenticationProvider](#daoauthenticationprovider)
|
||||
|
||||
|
||||
# Spring Security
|
||||
## Spring Security简介
|
||||
Spring Security作为一个安全框架,向使用者提供了用户认证、授权、常规攻击保护等功能。
|
||||
@@ -29,15 +74,83 @@ Spring Security支持FilterChainProxy,FilterChainProxy是一个由Spring Secur
|
||||
***FilterChainProxy是一个bean对象,通过被包含在DelegatingFilterProxy中。***
|
||||
|
||||
### SecurityFilterChain
|
||||
SecurityFilterChain通常被FilterChainProxy使用,用来决定在该次请求中调用那些Spring Security Filters。
|
||||
SecurityFilterChain通常被FilterChainProxy使用,用来决定在该次请求中调用哪个Spring Security Filters。
|
||||
|
||||
### SecurityFilters
|
||||
Security Filters通过SecurityFilterChain API被插入到FilterChainProxy中。
|
||||
|
||||
### 添加自定义filter到SecurityFilterChain
|
||||
大多数情况下,默认的security是足够的,但是,也允许向security filter chain中添加自定义过滤器。
|
||||
|
||||
如下是一个自定义过滤器的示例,示例中自定义filter会在认证后校验用户对于请求的租户是否拥有权限:
|
||||
```java
|
||||
import java.io.IOException;
|
||||
|
||||
import jakarta.servlet.Filter;
|
||||
import jakarta.servlet.FilterChain;
|
||||
import jakarta.servlet.ServletException;
|
||||
import jakarta.servlet.ServletRequest;
|
||||
import jakarta.servlet.ServletResponse;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.springframework.security.access.AccessDeniedException;
|
||||
|
||||
public class TenantFilter implements Filter {
|
||||
|
||||
@Override
|
||||
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
|
||||
HttpServletRequest request = (HttpServletRequest) servletRequest;
|
||||
HttpServletResponse response = (HttpServletResponse) servletResponse;
|
||||
|
||||
String tenantId = request.getHeader("X-Tenant-Id"); //(1)
|
||||
boolean hasAccess = isUserAllowed(tenantId); //(2)
|
||||
if (hasAccess) {
|
||||
filterChain.doFilter(request, response); //(3)
|
||||
return;
|
||||
}
|
||||
throw new AccessDeniedException("Access denied"); //(4)
|
||||
}
|
||||
|
||||
}
|
||||
```
|
||||
如下是自定义过滤器链的流程:
|
||||
1. 步骤从请求header中获取了tenantId
|
||||
2. 步骤校验了用户对于该tenant id代表的租户资源是否拥有访问权限
|
||||
3. 只有当用户对租户资源拥有访问权限时,才会继续调用filterChain中剩余的部分
|
||||
4. 如果用户没有访问权限,将会抛出AccessDeniedException
|
||||
|
||||
在定义完自定义过滤器后,还需要将自定义filter添加到Security Filter Chain中,添加代码如下所示例:
|
||||
```java
|
||||
@Bean
|
||||
SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
|
||||
http
|
||||
// ...
|
||||
.addFilterBefore(new TenantFilter(), AuthorizationFilter.class);
|
||||
return http.build();
|
||||
}
|
||||
```
|
||||
> 由于权限校验操作在身份认证之后,故而将TenantFilter加到AuthorizationFilter之前可以保证在执行TenantFilter时,所有的身份认证操作都已经执行完成。
|
||||
|
||||
> 在向SecurityFilterChain注册filter时,不应该将自定义filter注册为bean,因为spring会将注册为bean的filter对象都添加到内置容器中,这会造成注册为bean的filter被调用两次:一次被Spring Seurity调用,一次被container调用。
|
||||
|
||||
|
||||
|
||||
### 处理Security异常
|
||||
ExceptionTranslationFilter将认证异常和权限异常翻译为http response。
|
||||
> ExceptionTranslationFilter被插入到FilterChainProxy中,作为SecurityFilterChain中的一个。
|
||||
ExceptionTranslationFilter将`AccessDeniedException`和`AuthenticationException`翻译为http response。
|
||||
|
||||
> ExceptionTranslationFilter被插入到FilterChainProxy中,作为SecurityFilterChain中的一个filter。
|
||||
> 如果应用程序没有抛出AccessDeniedException或AuthenticationException,那么ExceptionTranslationFilter并不会做任何事情。
|
||||
|
||||
ExceptionTranslationFilter的处理流程:
|
||||
1. ExceptionTranslationFilter首先会调用`filterChain.doFilter`来执行后续的逻辑
|
||||
2. 在执行后续请求时,如果抛出了AuthenticationException,那么则会开启认证流程
|
||||
1. 清空SecurityContextHolder
|
||||
2. 保存HttpServletRequest,当认证通过时,还能重新发送原始请求
|
||||
3. `AuthenticationEntryPoint`用于重新请求用户的凭证,其可能会重定向到登录页面或是发送 `www-authenticate`header
|
||||
3. 在执行后续请求时,如果抛出了AccessDeniedException,那么会调用`AccessDeniedHandler`用于处理访问拒绝。
|
||||
4. 如果执行后续逻辑时,抛出了非`AuthenticationException`和`AccessDeniedException`的异常,那么异常将会被重新抛出,ExceptionTranslationFilter并不会针对其进行处理
|
||||
|
||||
```java
|
||||
// ExceptionTranslationFilter的伪代码
|
||||
try {
|
||||
@@ -51,6 +164,46 @@ try {
|
||||
}
|
||||
```
|
||||
|
||||
### 在多次认证之间保存request
|
||||
如果用户在发送请求访问需要认证的资源时,尚未经过身份认证,那么需要将本次请求保存起来,并且对用户进行认证,待用户认证通过之后,再重新执行保存的原始请求。在Spring Security中,是通过RequestCache来实现保存请求的。
|
||||
|
||||
#### RequestCache
|
||||
HttpServletRequest被保存在RequestCache中,当用户经过重新认证后,RequestCache将会用于重新发送原始请求。
|
||||
|
||||
在Spring Security中,在sendStartAuthentication时会像requestCache中存储request,并且重新执行认证流程,认证通过后,将会重新发送原始请求。
|
||||
|
||||
RequestCacheAwareFilter负责请求的重新发送。
|
||||
|
||||
默认情况下,会使用`HttpSessionRequestCache`,如下代码展示了HttpSessionRequestCache的自定义使用:
|
||||
```java
|
||||
@Bean
|
||||
DefaultSecurityFilterChain springSecurity(HttpSecurity http) throws Exception {
|
||||
HttpSessionRequestCache requestCache = new HttpSessionRequestCache();
|
||||
// 匹配本次request时,需要确保请求url参数中具有continue参数
|
||||
requestCache.setMatchingRequestParameterName("continue");
|
||||
http
|
||||
// ...
|
||||
.requestCache((cache) -> cache
|
||||
.requestCache(requestCache)
|
||||
);
|
||||
return http.build();
|
||||
}
|
||||
```
|
||||
|
||||
由于在认证失败时存储原始请求会占用内存,如果想要禁止在抛出AuthenticationException后,保存原始请求到RequestCache的行为,可以为SecurityFilterChain指定`NullRequestCache`:
|
||||
```java
|
||||
@Bean
|
||||
SecurityFilterChain springSecurity(HttpSecurity http) throws Exception {
|
||||
RequestCache nullRequestCache = new NullRequestCache();
|
||||
http
|
||||
// ...
|
||||
.requestCache((cache) -> cache
|
||||
.requestCache(nullRequestCache)
|
||||
);
|
||||
return http.build();
|
||||
}
|
||||
```
|
||||
|
||||
## Spring Security身份认证的结构
|
||||
### SecurityContextHolder
|
||||
Spring Security身份认证的核心模型,SecurityContextHolder包含有SecurityContext。
|
||||
|
||||
Reference in New Issue
Block a user