You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Spring Security添加路径变量校验过滤器的实现方法咨询

Spring Security 自定义过滤器实现路径编码校验方案

我刚好做过类似的需求,给你一套清晰的实现步骤,保证能在请求碰着Controller之前就完成校验,直接返回400错误,完全符合你的要求:

1. 编写自定义校验过滤器

先创建一个继承OncePerRequestFilter的过滤器——这个类是Spring官方提供的,能确保每个请求只会被过滤一次,特别适合这种前置校验的场景。

import org.springframework.web.filter.OncePerRequestFilter;
import org.springframework.util.AntPathMatcher;
import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Map;

public class RegionCodeValidationFilter extends OncePerRequestFilter {

    // 用Spring的路径匹配器提取路径里的变量,比自己拆字符串靠谱多了
    private final AntPathMatcher pathMatcher = new AntPathMatcher();

    // 如果你的校验逻辑需要调用其他服务(比如查数据库拿合法编码列表),可以在这里注入依赖
    // private final RegionCodeValidator codeValidator;

    // 构造函数注入依赖(如果有的话)
    // public RegionCodeValidationFilter(RegionCodeValidator codeValidator) {
    //     this.codeValidator = codeValidator;
    // }

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        // 定义需要校验的路径模式,比如所有带国家码/地区码的REST接口
        String targetPathPattern = "/{countryCode}/{regionCode}/**";
        
        // 先判断当前请求路径是否符合我们要校验的模式
        if (pathMatcher.match(targetPathPattern, request.getRequestURI())) {
            // 提取路径里的国家码和地区码
            Map<String, String> pathVars = pathMatcher.extractUriTemplateVariables(targetPathPattern, request.getRequestURI());
            String countryCode = pathVars.get("countryCode");
            String regionCode = pathVars.get("regionCode");

            // 这里替换成你的实际校验逻辑:对比路径编码和本地编码是否一致
            // 比如本地编码是从请求头拿的,或者是服务端预定义的合法列表
            if (!isValidCodeCombination(countryCode, regionCode)) {
                // 校验不通过,直接返回400
                response.setStatus(HttpServletResponse.SC_BAD_REQUEST);
                response.setContentType("application/json");
                response.getWriter().write("{\"error\": \"无效的国家/地区编码组合\"}");
                return; // 终止过滤链,不让请求继续往下走
            }
        }

        // 校验通过,放行到后续过滤器和Controller
        filterChain.doFilter(request, response);
    }

    // 模拟你的编码校验逻辑,记得换成真实业务规则
    private boolean isValidCodeCombination(String countryCode, String regionCode) {
        // 示例:只允许中国(CN)上海(SH)的编码组合
        return "CN".equals(countryCode) && "SH".equals(regionCode);
    }
}

2. 将过滤器注册到Spring Security过滤链

接下来要把这个过滤器加到Spring Security的流程里,关键是选对顺序——必须在请求到Controller之前执行,同时要和表单认证的过滤器配合好:

方式1:使用SecurityFilterChain(Spring Boot 2.7+ 官方推荐)

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;

@Configuration
public class SecurityConfig {

    // 如果过滤器需要依赖注入,用构造函数或者@Autowired注入
    // private final RegionCodeValidationFilter regionCodeValidationFilter;

    // public SecurityConfig(RegionCodeValidationFilter regionCodeValidationFilter) {
    //     this.regionCodeValidationFilter = regionCodeValidationFilter;
    // }

    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        http
            // 保留你原有的表单认证配置
            .formLogin(form -> form
                .loginPage("/login")
                .permitAll()
            )
            // 保留你原有的授权规则
            .authorizeHttpRequests(auth -> auth
                .requestMatchers("/public/**").permitAll()
                .anyRequest().authenticated()
            )
            // 把自定义过滤器加到表单认证过滤器之前,确保所有请求先过编码校验
            .addFilterBefore(regionCodeValidationFilter(), UsernamePasswordAuthenticationFilter.class);

        return http.build();
    }

    // 把自定义过滤器注册成Spring Bean,方便依赖注入和生命周期管理
    @Bean
    public RegionCodeValidationFilter regionCodeValidationFilter() {
        return new RegionCodeValidationFilter();
        // 如果有校验服务依赖,就传进去:return new RegionCodeValidationFilter(codeValidator);
    }
}

方式2:使用WebSecurityConfigurerAdapter(旧版兼容)

如果你的项目还在使用旧版配置,可以这么加:

import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.beans.factory.annotation.Autowired;

public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    private RegionCodeValidationFilter regionCodeValidationFilter;

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        super.configure(http);
        // 把自定义过滤器放在表单认证过滤器之前
        http.addFilterBefore(regionCodeValidationFilter, UsernamePasswordAuthenticationFilter.class);
        // 其他原有配置...
    }
}

3. 几个关键注意点

  • 过滤器顺序:用addFilterBefore把校验放在表单认证之前,能确保所有请求(包括登录请求)都先过编码校验;如果只需要对已认证的请求做校验,可以把过滤器放在UsernamePasswordAuthenticationFilter之后。
  • 路径匹配灵活性:AntPathMatcher支持各种复杂路径模式,比如/api/{countryCode}/**这种,完全能适配你的路径规则。
  • 错误返回效率:直接通过HttpServletResponse返回400比抛异常再用全局异常处理器处理更高效,适合这种简单的参数校验场景。
  • 依赖注入:如果校验逻辑需要调用其他服务,一定要把过滤器注册成Spring Bean,这样才能正常注入依赖。

这样配置完,所有符合路径模式的请求都会先经过编码校验,不符合规则的直接返回400,根本到不了Controller层,完美满足你的需求。

内容的提问来源于stack exchange,提问作者Dharita Chokshi

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.20 10:15:59