# 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的授权可以根据用户-权限、用户-角色、用户-角色-权限进行实现。 ### 基于方法的授权 基于方法的授权可以通过向方法添加权限注解来实现。