Spring Security 深度学习(五): 过滤器链与自定义过滤器

在这里插入图片描述

目录

    • 1. 引言:揭开Spring Security的内部奥秘
    • 2. Spring Security 过滤器链核心机制
      • 2.1 DelegatingFilterProxy:整合Spring与Servlet容器
      • 2.2 FilterChainProxy:管理安全过滤器链的“总管”
      • 2.3 Security Filters:核心安全功能的承载者
      • 2.4 过滤器执行顺序:了解其重要性
    • 3. 常用内置安全过滤器解析
    • 4. 自定义过滤器:实现个性化安全逻辑
      • 4.1 何时需要自定义过滤器?
      • 4.2 创建自定义过滤器的基本步骤
      • 4.3 将自定义过滤器插入过滤器链
    • 5. 实战演练:实现API Key认证与自定义过滤器
      • 5.1 定义API Key认证所需的组件
      • 5.2 实现 ApiKeyAuthenticationFilter
      • 5.3 实现 ApiKeyAuthenticationProvider
      • 5.4 更新 SecurityFilterChain,集成自定义过滤器
      • 5.5 注册API Key用户(或直接配置)
      • 5.6 测试API Key认证
    • 6. 常见陷阱与注意事项
    • 7. 阶段总结与进阶展望

1. 引言:揭开Spring Security的内部奥秘

在我们之前的学习中,我们主要通过HttpSecurity的链式API来配置Spring Security的功能,例如formLogin()authorizeHttpRequests()等。这些方法背后,Spring Security悄然地在Servlet容器的请求处理流程中插入了一系列自己的安全过滤器 (Security Filters)

本阶段的目标就是深入这些幕后工作,理解Spring Security的过滤器链 (Filter Chain) 机制。这不仅能帮助我们更好地调试和优化,更重要的是,它为我们提供了强大的可扩展性——当Spring Security提供的功能不足以满足特定需求时,我们可以自己编写自定义过滤器,并将其无缝集成到整个安全流程中。

2. Spring Security 过滤器链核心机制

Spring Security的核心是一个巨大的Servlet Filter,它通过代理模式将请求转发给内部的多个安全过滤器。

2.1 DelegatingFilterProxy:整合Spring与Servlet容器

  • 作用: DelegatingFilterProxy是Spring Security与传统的Servlet容器(如Tomcat)之间的桥梁。它本身是一个标准的Servlet Filter,但它不直接执行任何安全逻辑。
  • 工作原理: 当一个请求到来时,Servlet容器会调用DelegatingFilterProxydoFilter方法。DelegatingFilterProxy的唯一职责是从Spring应用上下文中查找一个名为springSecurityFilterChain的Bean(这就是我们之前配置的SecurityFilterChain最终转换而成的)并将请求委托给它。
  • 配置方式: 在Spring Boot中,spring-boot-starter-security会自动配置DelegatingFilterProxy为你服务。在传统的Servlet部署中,你需要在web.xml中手动配置它。

2.2 FilterChainProxy:管理安全过滤器链的“总管”

  • 作用: FilterChainProxy是Spring Security内部的核心过滤器,它负责管理和协调所有其他的Spring Security专用过滤器。你可以把它想象成一个大型的过滤器容器,它本身也是一个Filter,但它内部包含了一个甚至多个内部安全过滤器链

  • 多条过滤器链: FilterChainProxy可以根据请求的URL路径,匹配并选择一个合适的内部过滤器链来处理当前请求。例如,/api/**可能走一条无状态的JWT认证链,而/web/**走另一条Session-based认证链。这主要通过SecurityFilterChain Bean中定义的requestMatchers来实现。

  • 工作原理: FilterChainProxydoFilter方法会遍历其内部的SecurityFilterChain列表,找到第一个匹配当前请求的链。然后,它会依次调用该链中的所有安全过滤器。

    // 简化的FilterChainProxy工作原理
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {// 1. 获取请求URL// 2. 查找匹配该URL的 SecurityFilterChain (包含了多个Security Filter)// 3. 遍历匹配到的 SecurityFilterChain 中的每个 Security Filterfor (Filter securityFilter : matchedSecurityFilterChain) {securityFilter.doFilter(request, response, // ... 调用下一个过滤器);}// 4. 如果所有Security Filters都通过,最后调用原始的Filter Chain (即 Servlet Context 中的其他过滤器和Servlet)chain.doFilter(request, response);
    }
    

2.3 Security Filters:核心安全功能的承载者

FilterChainProxy内部,包含了Spring Security为各类安全功能提供的具体过滤器实例。每个过滤器都专注于处理特定的安全切面。例如:

  • 认证: 负责处理登录表单、HTTP Basic、JWT等。
  • 授权: 负责检查用户权限。
  • 会话管理: 负责Session的创建、销毁、并发控制。
  • 攻击防护: 负责CSRF、XSS等。

2.4 过滤器执行顺序:了解其重要性

Spring Security过滤器链中的过滤器是有严格顺序的。这个顺序非常重要,因为它决定了安全逻辑的执行流程。例如,认证必须发生在授权之前,异常处理必须发生在前面过滤器之后。

  • SecurityFilterChain中包含的过滤器的顺序是通过Spring Security内部机制严格管理的。当你使用http.addFilterBefore()http.addFilterAt()时,就是指示Spring Security在哪个已知过滤器之前或之后插入你的自定义过滤器。
  • 了解常用过滤器的顺序有助于你正确地定位和插入自定义过滤器。

3. 常用内置安全过滤器解析

了解以下几个Spring Security的核心过滤器,能帮助你更好地理解其内部工作机制:

  • SecurityContextHolderFilter (之前是SecurityContextPersistenceFilter)

    • 职责: 在每个请求开始时,从SecurityContextRepository(通常是HttpSessionSecurityContextRepository)加载SecurityContextSecurityContextHolder。在请求结束时,将SecurityContext保存回存储库,并清理SecurityContextHolder(防止内存泄漏)。
    • 位置: 通常在过滤器链的最前面。
  • LogoutFilter

    • 职责: 拦截对登出URL(/logout)的请求,执行登出操作(使Session失效、清除Remember-Me Token等)。
    • 位置: 在多数认证过滤器之前,确保用户可以随时登出。
  • UsernamePasswordAuthenticationFilter

    • 职责: 处理基于表单的用户名密码认证请求(默认拦截/login的POST请求)。它会从请求中获取用户名和密码,然后尝试通过AuthenticationManager进行认证。
    • 位置: 在认证流程的核心位置。
  • ExceptionTranslationFilter

    • 职责: 捕获过滤器链中抛出的AuthenticationException(认证失败)或AccessDeniedException(授权失败),并委托给相应的处理组件(AuthenticationEntryPointAccessDeniedHandler)进行处理。
    • 位置: 在所有认证和授权过滤器之后,负责统一的异常处理。
  • FilterSecurityInterceptor

    • 职责: 这是过滤器链中最后一个负责授权的过滤器。它根据已认证用户的权限和资源配置来决定是否允许访问该资源。它会委托给AccessDecisionManager进行最终的授权决策。
    • 位置: 过滤器链的尾部,在所有认证完成之后。

4. 自定义过滤器:实现个性化安全逻辑

4.1 何时需要自定义过滤器?

当Spring Security提供的标准认证和授权机制无法满足你的特定需求时,自定义过滤器就派上用场了。常见场景包括:

  • 自定义Token认证: 例如,实现API Key认证,从请求头中获取X-API-KEY进行验证。
  • 请求头处理: 从自定义请求头中提取信息(如租户ID),并存储到SecurityContext或请求属性中。
  • 多因素认证:UsernamePasswordAuthenticationFilter之后添加一层额外的验证。
  • 限流/防刷: 在请求到达Spring Security认证之前进行初步的限流检查。
  • 日志记录/审计: 记录特定安全事件或请求。

4.2 创建自定义过滤器的基本步骤

自定义过滤器通常继承OncePerRequestFilter或实现Filter接口。

  • OncePerRequestFilter (推荐): 确保你的过滤器在每个请求中只执行一次,即使它被包含在FilterChainProxy和Servlet容器的其他Filter中。这是编写Web应用过滤器的最佳实践。

基本结构:

package com.example.springsecuritystage1.filter;import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.web.filter.OncePerRequestFilter;import java.io.IOException;public class CustomLoggerFilter extends OncePerRequestFilter {@Overrideprotected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)throws ServletException, IOException {System.out.println("CustomLoggerFilter: Request received for " + request.getRequestURI());// 执行下一个过滤器或目标ServletfilterChain.doFilter(request, response);System.out.println("CustomLoggerFilter: Response sent for " + request.getRequestURI());}
}

4.3 将自定义过滤器插入过滤器链

一旦你创建了自定义过滤器,你需要告诉Spring Security把它放在过滤器链的哪个位置。这通过HttpSecurity的以下方法实现:

  • addFilterAfter(Filter filter, Class<? extends Filter> afterFilter) 将自定义过滤器放在指定Spring Security过滤器之后。
  • addFilterBefore(Filter filter, Class<? extends Filter> beforeFilter) 将自定义过滤器放在指定Spring Security过滤器之前。
  • addFilterAt(Filter filter, Class<? extends Filter> atFilter) 将自定义过滤器替换指定Spring Security过滤器所在的位置。

示例:

@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {http// ... 其他配置.addFilterBefore(new CustomLoggerFilter(), UsernamePasswordAuthenticationFilter.class)// ... 其他配置return http.build();
}

重要:

  • afterFilter, beforeFilter, atFilter 参数必须是Spring Security内置过滤器的class类型,例如UsernamePasswordAuthenticationFilter.class。你不能直接引用其Bean名称或自定义过滤器的class(除非那个自定义过滤器已经是Spring Security的一部分)。
  • 自定义过滤器本身通常被定义为一个@Bean,并在SecurityFilterChain配置中注入使用。

5. 实战演练:实现API Key认证与自定义过滤器

我们将实现一个简单的API Key认证机制,适用于保护一些内部API。

需求:

  • 某些API(如/api/v2/**)需要通过API Key进行认证。
  • API Key通过X-API-KEY请求头传递。
  • 如果API Key有效,用户被认证为拥有API_KEY_AUTH权限。

5.1 定义API Key认证所需的组件

A. API Key认证Token (ApiKeyAuthenticationToken.java)

为了在Spring Security的认证流程中传递API Key信息,我们需要创建一个Authentication接口的实现类。

package com.example.springsecuritystage1.security.token;import org.springframework.security.authentication.AbstractAuthenticationToken;
import org.springframework.security.core.GrantedAuthority;import java.util.Collection;// API Key 认证的Token
public class ApiKeyAuthenticationToken extends AbstractAuthenticationToken {private final String apiKey; // 认证凭证 (API Key本身)private final Object principal; // 认证主体 (通常是用户名或一个UserDetails对象)public ApiKeyAuthenticationToken(String apiKey) {super(null); // 未认证时,没有权限this.apiKey = apiKey;this.principal = apiKey; // 初始时,主体就是API Key字符串setAuthenticated(false); // 默认未认证}public ApiKeyAuthenticationToken(String apiKey, Object principal, Collection<? extends GrantedAuthority> authorities) {super(authorities); // 认证成功后,拥有权限this.apiKey = apiKey;this.principal = principal; // 认证成功后,主体可以是UserDetailssetAuthenticated(true); // 认证成功}@Overridepublic Object getCredentials() {return apiKey;}@Overridepublic Object getPrincipal() {return principal;}
}

5.2 实现 ApiKeyAuthenticationFilter

这个过滤器负责从请求头中提取API Key,并将其包装成ApiKeyAuthenticationToken提交给AuthenticationManager

package com.example.springsecuritystage1.filter;import com.example.springsecuritystage1.security.token.ApiKeyAuthenticationToken;
import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.web.filter.OncePerRequestFilter;import java.io.IOException;// API Key 认证过滤器
public class ApiKeyAuthenticationFilter extends OncePerRequestFilter {private static final String API_KEY_HEADER = "X-API-KEY";private final AuthenticationManager authenticationManager;public ApiKeyAuthenticationFilter(AuthenticationManager authenticationManager) {this.authenticationManager = authenticationManager;}@Overrideprotected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)throws ServletException, IOException {// 1. 从请求头中提取 API KeyString apiKey = request.getHeader(API_KEY_HEADER);// 如果没有API Key,或者已经有认证信息,则直接传递给下一个过滤器// Spring Security 已经有认证信息了,通常不再需要API Key认证if (apiKey == null || apiKey.isEmpty() || SecurityContextHolder.getContext().getAuthentication() != null) {filterChain.doFilter(request, response);return;}try {// 2. 创建未认证的 ApiKeyAuthenticationTokenApiKeyAuthenticationToken authenticationToken = new ApiKeyAuthenticationToken(apiKey);// 3. 将Token提交给 AuthenticationManager 进行认证// AuthenticationManager会找到对应的AuthenticationProvider进行处理Authentication authentication = authenticationManager.authenticate(authenticationToken);// 4. 认证成功,将认证信息存入 SecurityContextHolderSecurityContextHolder.getContext().setAuthentication(authentication);System.out.println("API Key authenticated successfully for: " + apiKey);} catch (Exception e) {// 认证失败,清除SecurityContext(如果之前有的话),并返回401 UnauthorizedSecurityContextHolder.clearContext();response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);response.getWriter().write("API Key authentication failed: " + e.getMessage());return; // 阻止请求继续往下走}// 继续过滤器链filterChain.doFilter(request, response);}
}

5.3 实现 ApiKeyAuthenticationProvider

这个Provider负责验证API Key的有效性,并创建已认证的ApiKeyAuthenticationToken

package com.example.springsecuritystage1.security.provider;import com.example.springsecuritystage1.security.token.ApiKeyAuthenticationToken;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.stereotype.Component;import java.util.Collections;
import java.util.HashSet;
import java.util.Set;// API Key 认证提供者
@Component // 注册为Spring Bean
public class ApiKeyAuthenticationProvider implements AuthenticationProvider {// 实际生产环境,这里的API Key应该从数据库、配置中心加载,并支持多个// 这里仅为演示硬编码一个有效API Keyprivate static final String VALID_API_KEY = "my_super_secret_api_key_123";@Overridepublic Authentication authenticate(Authentication authentication) throws AuthenticationException {// 1. 检查传入的Token类型是否是 ApiKeyAuthenticationTokenApiKeyAuthenticationToken apiKeyAuthenticationToken = (ApiKeyAuthenticationToken) authentication;String apiKey = (String) apiKeyAuthenticationToken.getCredentials();// 2. 验证 API Keyif (VALID_API_KEY.equals(apiKey)) {// 认证成功,构建一个包含权限的已认证TokenSet<SimpleGrantedAuthority> authorities = new HashSet<>();authorities.add(new SimpleGrantedAuthority("API_KEY_AUTH")); // 赋予特定权限// 这里也可以根据API Key的类型赋予不同的角色或权限// principal 可以是 UserDetails 对象,这里为了简化直接用API Key作为主体return new ApiKeyAuthenticationToken(apiKey, apiKey, authorities);} else {// 认证失败throw new BadCredentialsException("Invalid API Key");}}@Overridepublic boolean supports(Class<?> authentication) {// 只有 ApiKeyAuthenticationToken 类型的认证请求才由这个Provider处理return ApiKeyAuthenticationToken.class.isAssignableFrom(authentication);}
}

5.4 更新 SecurityFilterChain,集成自定义过滤器

我们需要将ApiKeyAuthenticationFilter添加到过滤器链。由于它是处理API Key认证的,我们应该把它放在传统的UsernamePasswordAuthenticationFilter之前,让它优先处理API Key请求。

我们将为/api/v2/**路径专门配置一个需要API_KEY_AUTH权限的授权规则。

package com.example.springsecuritystage1.config;// ... 省略其他 imports
import com.example.springsecuritystage1.filter.ApiKeyAuthenticationFilter; // 导入
import com.example.springsecuritystage1.security.provider.ApiKeyAuthenticationProvider; // 导入
import org.springframework.security.authentication.ProviderManager; // 导入
import org.springframework.security.authentication.dao.DaoAuthenticationProvider; // 导入
import org.springframework.security.core.userdetails.UserDetailsService; // 导入
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; // 导入@Configuration
@EnableWebSecurity
@EnableMethodSecurity(prePostEnabled = true, securedEnabled = true, jsr250Enabled = true)
public class CustomSecurityConfig {private final DataSource dataSource;private final UserDetailsService userDetailsService; // 注入 UserDetailsServiceprivate final PasswordEncoder passwordEncoder; // 注入 PasswordEncoderprivate final ApiKeyAuthenticationProvider apiKeyAuthenticationProvider; // 注入 API Key Providerpublic CustomSecurityConfig(DataSource dataSource, UserDetailsService userDetailsService, PasswordEncoder passwordEncoder,ApiKeyAuthenticationProvider apiKeyAuthenticationProvider) {this.dataSource = dataSource;this.userDetailsService = userDetailsService;this.passwordEncoder = passwordEncoder;this.apiKeyAuthenticationProvider = apiKeyAuthenticationProvider;}@Beanpublic PasswordEncoder passwordEncoder() { /* ... */ return new BCryptPasswordEncoder(); }@Beanpublic UserDetailsService userDetailsService() { /* ... */ return new CustomUserDetailsService(sysUserMapper); }@Beanpublic PersistentTokenRepository persistentTokenRepository() { /* ... */ return tokenRepository; }// 在这里配置 AuthenticationManager,它将包含 DaoAuthenticationProvider 和 ApiKeyAuthenticationProvider@Beanpublic ProviderManager authenticationManager() {DaoAuthenticationProvider daoProvider = new DaoAuthenticationProvider();daoProvider.setUserDetailsService(userDetailsService);daoProvider.setPasswordEncoder(passwordEncoder);// ProviderManager 可以接收多个 AuthenticationProviderreturn new ProviderManager(daoProvider, apiKeyAuthenticationProvider);}@Beanpublic SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {http.authorizeHttpRequests(authorize -> authorize.requestMatchers("/css/**", "/js/**", "/images/**", "/login", "/register", "/public/**").permitAll().requestMatchers("/admin/**").hasAnyAuthority("ROLE_ADMIN", "USER_MANAGE").requestMatchers("/user/**").hasAnyAuthority("ROLE_USER", "ROLE_ADMIN", "USER_VIEW")// 配置 /api/v2/** 路径需要 API_KEY_AUTH 权限.requestMatchers("/api/v2/**").hasAuthority("API_KEY_AUTH") .requestMatchers(HttpMethod.GET, "/api/products/**").hasAnyAuthority("PRODUCT_READ", "ROLE_ADMIN").requestMatchers(HttpMethod.POST, "/api/products").hasAuthority("PRODUCT_CREATE").requestMatchers(HttpMethod.PUT, "/api/products/**").hasAuthority("PRODUCT_EDIT").requestMatchers(HttpMethod.DELETE, "/api/products/**").hasAuthority("PRODUCT_DELETE").anyRequest().authenticated()).formLogin(form -> form.loginPage("/login").defaultSuccessUrl("/user/profile", true).permitAll()).logout(logout -> logout.logoutUrl("/logout").logoutSuccessUrl("/login?logout").invalidateHttpSession(true).deleteCookies("JSESSIONID", "remember-me").permitAll()).rememberMe(remember -> remember.key("myUniqueSecretKey").tokenRepository(persistentTokenRepository()).tokenValiditySeconds(86400 * 30).userDetailsService(userDetailsService)).sessionManagement(session -> session.maximumSessions(1).maxSessionsPreventsLogin(false).expiredUrl("/login?expired")).exceptionHandling(exception -> exception.accessDeniedPage("/access-denied")).csrf(Customizer.withDefaults())// 将自定义过滤器添加到 UsernamePasswordAuthenticationFilter 之前// 这样 API Key 认证可以在表单登录认证之前先进行.addFilterBefore(new ApiKeyAuthenticationFilter(authenticationManager()), UsernamePasswordAuthenticationFilter.class);return http.build();}@Beanpublic HttpSessionEventPublisher httpSessionEventPublisher() {return new HttpSessionEventPublisher();}
}

重要更新:

  1. 我们明确定义了一个ProviderManager Bean作为AuthenticationManager。这个ProviderManager现在包含了DaoAuthenticationProvider(用于处理用户名密码登录)和我们自定义的ApiKeyAuthenticationProvider。这样,AuthenticationManager就能同时支持两种认证方式。
  2. ApiKeyAuthenticationFilter通过addFilterBefore()方法被添加到UsernamePasswordAuthenticationFilter之前,确保/api/v2/**的请求可以优先通过API Key认证。
  3. authorizeHttpRequests中,为/api/v2/**路径添加了hasAuthority("API_KEY_AUTH")的授权规则。

5.5 注册API Key用户(或直接配置)

由于这里的API Key是硬编码在ApiKeyAuthenticationProvider中的,我们不需要注册。

添加一个测试API端点:

v2/ApiController.java

package com.example.springsecuritystage1.controller;import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;@RestController
@RequestMapping("/api/v2")
public class ApiV2Controller {@GetMapping("/secret-data")@PreAuthorize("hasAuthority('API_KEY_AUTH')") // 或者URL级别已经限制了public String getSecretData() {return "This is top secret data, accessed via API Key!";}@GetMapping("/public-data")// 假设这个API V2下的公共数据,理论上也需要API key认证,只是没有更细的权限public String getPublicData() {return "This is public data, but still under API Key protection.";}
}

5.6 测试API Key认证

  1. 启动应用。
  2. 不带API Key访问: 使用浏览器或Postman访问 http://localhost:8080/api/v2/secret-data。你应该会收到 401 Unauthorized 响应,因为ApiKeyAuthenticationFilter会捕获认证失败。
  3. 携带错误API Key访问: 使用Postman,在请求头中添加 X-API-KEY: wrong_key。发送请求,你应该仍然收到 401 Unauthorized
  4. 携带正确API Key访问: 使用Postman,在请求头中添加 X-API-KEY: my_super_secret_api_key_123。发送请求。
    • 你应该会收到200 OK响应,内容为“This is top secret data, accessed via API Key!”或者“This is public data, but still under API Key protection.”。这表示API Key认证成功。
  5. 验证URL授权: 如果你尝试访问一个需要API Key认证的API (如 /api/v2/secret-data),但提供的API Key只授予了 SOME_OTHER_AUTH 权限,理论上应该收到 403 Forbidden。但在我们的实现中 API_KEY_AUTH 是唯一的权限。

6. 常见陷阱与注意事项

  • 过滤器顺序: 插入自定义过滤器时,其位置至关重要。错误的位置可能导致认证失败、无限重定向或绕过安全防护。仔细阅读Spring Security的过滤器链文档,理解每个内置过滤器的职责。
  • AuthenticationManager的注入: 如果自定义了Filter,并且它需要执行认证(调用authenticate()),那么你需要一个AuthenticationManager实例。在Spring Security 6.x+中,AuthenticationManager默认是一个Bean(通常是ProviderManager的实例)。我们通过在CustomSecurityConfig中明确定义一个AuthenticationManager Bean来确保它能包含我们自定义的AuthenticationProvider
  • 异常处理: 在自定义过滤器中,如果认证失败(通常是抛出AuthenticationException),你可能需要手动设置HTTP响应状态码(如401 Unauthorized),并阻止请求继续往下传,否则可能会被后面的过滤器(如ExceptionTranslationFilter)处理,导致不符合预期。
  • OncePerRequestFilter 始终继承这个类,以避免过滤器在同一个请求中被多次执行。
  • CSRF与自定义过滤器: 如果你的自定义过滤器处理的是POST请求,并且它在CsrfFilter之前执行,那么你可能需要手动处理CSRF Token,或者确保你的自定义过滤器能够容忍CSRF Token在CsrfFilter之后才被校验。大多数情况下,自定义认证过滤器应该放在CsrfFilter之前,因为如果请求本身就无法认证,就没有CSRF校验的必要。
  • SecurityContextHolder的清理: 自定义过滤器如果直接操作了SecurityContextHolder,记得在请求结束后(或异常发生时)进行清理,以避免线程之间数据污染。SecurityContextHolderFilter会默认进行清理。

7. 阶段总结与进阶展望

恭喜你完成了Spring Security深度学习的第五阶段!你现在已经掌握了:

  • Spring Security过滤器链的核心机制,包括DelegatingFilterProxyFilterChainProxy的工作原理。
  • Spring Security中常见内置安全过滤器的职责和执行顺序。
  • 如何创建和集成自定义安全过滤器到Spring Security过滤器链中的特定位置。
  • 通过API Key认证的实战案例,加深了对AuthenticationTokenAuthenticationProviderAuthenticationManager的理解。

你现在对Spring Security的底层工作方式有了更清晰的认识,能够根据业务需求,设计并实现更加个性化和灵活的安全功能。

在下一阶段,我们将迎接现代Web应用中最流行的认证方案之一:RESTful API 安全与 JWT (JSON Web Token)。我们将深入学习JWT的生成、验证、集成,以及如何构建一个完全无状态的API认证系统,这对于微服务架构尤其重要。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/pingmian/95422.shtml
繁体地址,请注明出处:http://hk.pswp.cn/pingmian/95422.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

微软GraphRAG 端到端使用及自用工具类

文章目录一. 环境准备1.安装 Python 环境2.安装依赖3.配置 LLM API Key二. 初始化项目三. 文档上传 & 索引构建四. 问答&#xff08;CLI 方式&#xff09;示例&#xff1a;五. 代码中调用 GraphRAG工具概览核心工具详解1. simple_graphrag_integration.py - 智能问答核心2.…

sqlserver2008导入excel表数据遇到的问题

1.如果表格为.xlsx格式时可能会提示“没有为此链接管理提供列”&#xff0c;无法点击下一步的话&#xff0c;建议可以使用.csv格式 .csv格式可能也会存在此提示&#xff0c;但是可以不用管 2.导入.csv数据时&#xff0c;字段为int时&#xff0c;填null导入不进去的话可以给个0作…

Unity游戏打包——打包流程

本文由 NRatel 历史笔记整理而来&#xff0c;如有错误欢迎指正。一、基本流程二、组合步骤把上述每步做成独立的输入输出逻辑 y fuc(x)然后&#xff0c;控制/组合其过程&#xff0c;可灵活产生不同的流程&#xff1a;1、单渠道出测试包2、单渠道出正式包3、单渠道包热更三、构…

卷积神经网络(二):手写数字识别项目(一)

文章目录手写数字识别项目一、准备数据集二、定义模型三、模型训练3.1 导入依赖库3.2 设备设置&#xff08;CPU/GPU 自动选择&#xff09;3.3 超参数定义3.4数据集准备1.获取数据集2.划分训练集与验证集3.创建 DataLoader&#xff08;按批次加载数据&#xff09;3.5模型初始化与…

批量给文件夹添加文件v2【件批量复制工具】

代码功能介绍 这个代码的功能就是一个&#xff0c;给某个文件夹里面添加某个文件&#xff08;含父级文件夹下的每一个子文件夹&#xff09; 举个例子&#xff0c;父级文件夹是&#xff1a;“D:\Desktop\1&#xff0c;要添加的文件路径是&#xff1a;D:\1.txt” 则最后会把文件…

Qt实现2048小游戏:看看AI如何评估棋盘策略实现“人机合一

2048 是一款经典的数字益智游戏,其简单的规则背后蕴含着丰富的策略性。该项目不仅完整实现了 2048 的核心玩法,还包含了一个基于启发式评估和蒙特卡洛方法的智能 AI 玩家。 我们将从项目整体架构入手,逐一解析游戏核心逻辑、UI 渲染、事件处理、AI 策略等关键模块,并通过展…

封装红黑树实现mysetmymap

1. 源码分析 set实例化rb_tree时第二个模板参数给的是key&#xff0c;map实例化rb_tree时第⼆个模板参数给的是 pair<const key,T>&#xff0c;这样一颗红黑树既可以实现key搜索场景的set&#xff0c;也可以实现key/value搜索场 景的map源码里面模板参数是用T代表value&…

以OWTB为核心以客户为基础的三方仓运配一体化平台分析V0.2

一、系统概述以OWTB&#xff08;Order-Warehouse-Transportation-Billing&#xff0c;订单-仓储-运输-结算&#xff09;为核心的三方仓运配一体化平台&#xff0c;是专为第三方物流企业打造的深度定制化解决方案。该平台以第三方仓运配为主线&#xff0c;以多客户/多SKU/个性化…

技术框架之脚手架实现

一、 序言在日常的企业级Java开发中&#xff0c;我们经常会发现自己在重复地做着一些项目初始化工作&#xff1a;创建相似的项目结构、引入一堆固定的依赖包、编写通用的配置文件、拷贝那些几乎每个项目都有的基础工具类和日志配置。这些工作不仅枯燥乏味&#xff0c;而且容易出…

小迪安全v2023学习笔记(七十七讲)—— 业务设计篇隐私合规检测重定向漏洞资源拒绝服务

文章目录前记WEB攻防——第七十七天业务设计篇&隐私合规检测&URL重定向&资源拒绝服务&配合项目隐私合规 - 判断规则&检测项目介绍案例演示URL重定向 - 检测判断&钓鱼配合介绍黑盒测试看业务功能看参数名goole语法搜索白盒测试跳转URL绕过思路钓鱼配合资…

用AI做旅游攻略,真能比人肉整理靠谱?

大家好&#xff0c;我是极客团长&#xff01; 作为一个沉迷研究 “AI 工具怎么渗透日常生活” 的科技博主&#xff0c;我开了个 AI 解决生活小事系列。 前两期聊了用 AI 写新闻博客、扒商业报告&#xff0c;后台一堆人催更&#xff1a;能不能搞点接地气的&#xff1f;比如&am…

Axure RP 9 Mac 交互原型设计

原文地址&#xff1a;Axure RP 9 Mac 交互原型设计 安装教程 Axure RP 9是一款功能强大的原型设计和协作工具。 它不仅能够帮助用户快速创建出高质量的原型设计&#xff0c;还能促进团队成员之间的有效协作&#xff0c;从而极大地提高数字产品开发的效率和质量。 拥有直观易…

多线程——线程状态

目录 1.线程的状态 1.1 NEW 1.2 RUNNABLE 1.3 BLOCKED 1.4 WAITING 1.5 TIMED_WAITING 1.6 TERMINATED 2.线程状态的相互转换 在上期的学习中&#xff0c;已经理解线程的启动&#xff08;start()&#xff09;、休眠&#xff08;sleep()&#xff09;、中断&#xff08;i…

IMX6ULL的设备树文件简析

先分析一个完整的设备树&#xff0c;是怎么表达各种外设信息的。以imux6ull开发板为例进行说明。这个文件里就一个设备信息才这么点内容&#xff0c;是不是出问题了&#xff1f;当然不是&#xff0c;我们知道dts文件是可包含的&#xff0c;所以&#xff0c;最终形成的一个完整文…

【ARM】PACK包管理

1、 文档目标对 pack 包的管理有更多的了解。2、 问题场景客户在安装了过多的 pack 包导致软件打开比较慢&#xff0c;各种 pack 包颜色的区别&#xff0c;及图标不同。3、软硬件环境1&#xff09;、软件版本&#xff1a;Keil MDK 5.392&#xff09;、电脑环境&#xff1a;Wind…

【Kubernetes】知识点4

36. 说明K8s中Pod级别的Graceful Shutdown。答&#xff1a;Graceful Shutdown&#xff08;优雅关闭&#xff09;是指当 Pod 需要终止时&#xff0c;系统给予运行中的容器一定的时间来等待业务的应用的正常关闭&#xff08;如保存数据、关闭连接、释放资源等&#xff09;&#x…

Paraverse平行云实时云渲染助力第82届威尼斯电影节XR沉浸式体验

今年&#xff0c;Paraverse平行云实时云渲染平台LarkXR&#xff0c;为享有盛誉的第82届威尼斯国际电影节&#xff08;8月27日至9月6日&#xff09;带来沉浸式体验。 LarkXR助力我们的生态伙伴FRENCH TOUCH FACTORY&#xff0c;实现ITHACA容积视频的XR交互演示&#xff0c;从意大…

大数据开发计划表(实际版)

太好了&#xff01;我将为你生成一份可打印的PDF版学习计划表&#xff0c;并附上项目模板与架构图示例&#xff0c;帮助你更直观地执行计划。 由于当前环境无法直接生成和发送文件&#xff0c;我将以文本格式为你完整呈现&#xff0c;你可以轻松复制到Word或Markdown中&#xf…

GitLab 18.3 正式发布,更新多项 DevOps、CI/CD 功能【二】

沿袭我们的月度发布传统&#xff0c;极狐GitLab 发布了 18.3 版本&#xff0c;该版本带来了通过直接转移进行迁移、CI/CD 作业令牌的细粒度权限控制、自定义管理员角色、Kubernetes 1.33 支持、通过 API 让流水线执行策略访问 CI/CD 配置等几十个重点功能的改进。下面是对部分重…

Docker学习笔记(二):镜像与容器管理

Docker 镜像 最小的镜像 hello-world 是 Docker 官方提供的一个镜像&#xff0c;通常用来验证 Docker 是否安装成功。 先通过 docker pull 从 Docker Hub 下载它。 [rootdocker ~]# docker pull hello-world Using default tag: latest latest: Pulling from library/hello-wor…