119 lines
5.1 KiB
Markdown
119 lines
5.1 KiB
Markdown
# Spring Security
|
||
Spring Security提供了authentication(认证)、authorization(授权)、protection against common attacks(防御常见攻击)的功能。
|
||
|
||
Spring Security提供了针对如下常见攻击的防御:
|
||
- CSRF
|
||
- Http Headers
|
||
- Http Requests
|
||
|
||
## 默认自动装配
|
||
当web项目引入Spring Security依赖后,如果不做任何特殊处理,Spring Security会默认为项目配置一个登录校验。当用户没有进行登录而访问页面时,会拦截用户请求返回一个登录页,在用户登录成功之后,再会将url重定向到用户先前请求的页面。
|
||
|
||
### Spring Security默认为项目做的操作
|
||
1. 保护web项目的url,Spring Security要求与http接口进行的任何交互都需要进行身份认证
|
||
2. Spring Security会在web项目启动时生成一个默认的用户,用户名为user,用户密码会通过日志打出
|
||
3. Spring Security会为项目生成默认的登录和注销页面,并提供了基本登录流程和注销流程
|
||
4. 对于http请求,如果在没有进行身份认证的情况下,会重定向到登录页面
|
||
|
||
## Spring Security底层结构
|
||
Spring Security底层通过Filter链来实现。
|
||
|
||
### DelegatingFilterProxy
|
||
spring boot提供了Filter的实现类DelegatingFilterProxy,该类将实际的过滤逻辑委托给另一个filter bean对象。
|
||
|
||
通过DelegatingFilterProxy,可以将servlet container和spring容器结合起来,将DelegatingFilterProxy对象作为filter注册到servlet container中,然后DelegatingFilterProxy把实际过滤逻辑委托给spring context中的bean对象,如此可以通过向spring容器中注册bean对象来自定义filter chain逻辑。
|
||
|
||
### SecurityFilterChain
|
||
DelegationFilterProxy将filter逻辑委托给了FilterChainProxy。
|
||
|
||
而FilterProxyChain则是包含了SecurityFilterChain(并且可以包含多个SecurityFilterChain)。SecurityFilterChain中包含的才是多个filter bean对象。
|
||
|
||
如果FilterChainProxy中注册了多个SecurityFilterChain,那么将由FilterChainProxy来决定实际调用哪个SecurityFilterChain。只有匹配的第一个SecurityFilterChain会被实际调用。
|
||
|
||
> 如果当spring容器中存在多个SecurityFilterChian bean对象,那么可以通过@Order注解来指定SecurityFilterChain bean对象的顺序,从而调整不同SecurityFilterChain的优先级
|
||
|
||
示例如下:
|
||
```java
|
||
@Configuration
|
||
@EnableWebSecurity
|
||
public class SecurityConfig {
|
||
|
||
@Bean
|
||
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
|
||
http
|
||
.csrf(Customizer.withDefaults())
|
||
.authorizeHttpRequests(authorize -> authorize
|
||
.anyRequest().authenticated()
|
||
)
|
||
.httpBasic(Customizer.withDefaults())
|
||
.formLogin(Customizer.withDefaults());
|
||
return http.build();
|
||
}
|
||
|
||
}
|
||
```
|
||
上述代码注册的SecurityFilterChain将拥有如下顺序:
|
||
| Filter | Added by |
|
||
| :-: | :-: |
|
||
| CsrfFilter | HttpSecurity#csrf |
|
||
| UsernamePasswordAuthenticationFilter | HttpSecurity#formLogin |
|
||
| BasicAuthenticationFilter | HttpSecurity#httpBasic |
|
||
| AuthorizationFilter | HttpSecurity#authorizeHttpRequests |
|
||
|
||
> 通常,security filter chain都是先执行authentication再执行authorization
|
||
|
||
### 自定义Filter
|
||
除了使用Spring Security预置的filter外,还可以使用自定义的filter,自定义filter使用示例如下:
|
||
```java
|
||
// Filter定义
|
||
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)
|
||
}
|
||
|
||
}
|
||
```
|
||
```java
|
||
// 将自定义filter添加到SecurityFilterChain中
|
||
@Bean
|
||
SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
|
||
http
|
||
// ...
|
||
.addFilterBefore(new TenantFilter(), AuthorizationFilter.class);
|
||
return http.build();
|
||
}
|
||
```
|
||
## 授权
|
||
对于spring security,其授权可以分为基于request的授权和基于方法的授权。
|
||
|
||
### 基于request的授权
|
||
基于request的授权可以根据用户-权限、用户-角色、用户-角色-权限进行实现。
|
||
|
||
### 基于方法的授权
|
||
基于方法的授权可以通过向方法添加权限注解来实现。
|
||
|
||
|