4.7 KiB
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默认为项目做的操作
- 保护web项目的url,Spring Security要求与http接口进行的任何交互都需要进行身份认证
- Spring Security会在web项目启动时生成一个默认的用户,用户名为user,用户密码会通过日志打出
- Spring Security会为项目生成默认的登录和注销页面,并提供了基本登录流程和注销流程
- 对于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的优先级
示例如下:
@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使用示例如下:
// 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)
}
}
// 将自定义filter添加到SecurityFilterChain中
@Bean
SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
// ...
.addFilterBefore(new TenantFilter(), AuthorizationFilter.class);
return http.build();
}