From 254062c161354143525318e3ff36c79722e34af3 Mon Sep 17 00:00:00 2001 From: asahi Date: Tue, 12 Mar 2024 00:41:28 +0800 Subject: [PATCH] =?UTF-8?q?=E9=98=85=E8=AF=BBspring=20security=E6=96=87?= =?UTF-8?q?=E6=A1=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- spring/Spring Security/spring_security_sgg.md | 111 ++++++++++++++++++ 1 file changed, 111 insertions(+) create mode 100644 spring/Spring Security/spring_security_sgg.md diff --git a/spring/Spring Security/spring_security_sgg.md b/spring/Spring Security/spring_security_sgg.md new file mode 100644 index 0000000..8b96ed5 --- /dev/null +++ b/spring/Spring Security/spring_security_sgg.md @@ -0,0 +1,111 @@ +# 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(); +} +``` + + +