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

Spring中FilterRegistrationBean仅触发首个过滤器问题求助

问题描述

实现Token验证过滤器系统时,通过FilterRegistrationBean为三类客户端(company/admin/customer)的控制器配置了对应路径的过滤器,但在Swagger UI发起请求时,仅/company/*路径的过滤器生效,/admin/*和/customer/*路径的过滤器完全不触发。

相关代码如下:

FilterRegistrationBean配置类

@Component
public class CompanyFilterComponent {

    @Bean(name = "companyFilter")
    public FilterRegistrationBean<ClientFilter> companyFilter(JwtUtil jwtUtil) {
        System.out.println("company");
        FilterRegistrationBean<ClientFilter> filterRegistrationBean = new FilterRegistrationBean<>();
        filterRegistrationBean.setFilter(new ClientFilter(jwtUtil,ClientType.Company));
        filterRegistrationBean.setOrder(0);
        filterRegistrationBean.addUrlPatterns("/company/*");
        return filterRegistrationBean;
    }


    @Bean(name = "adminFilter")
    public FilterRegistrationBean<ClientFilter> adminFilter(JwtUtil jwtUtil) {
        System.out.println("admin");
        FilterRegistrationBean<ClientFilter> filterRegistrationBean = new FilterRegistrationBean<>();
        filterRegistrationBean.setFilter(new ClientFilter(jwtUtil, ClientType.Administrator));
        filterRegistrationBean.setOrder(1);
        filterRegistrationBean.addUrlPatterns("/admin/*");
        
        return filterRegistrationBean;

    }
    @Bean(name = "customerFilter")
    public FilterRegistrationBean<ClientFilter> customerFilter(JwtUtil jwtUtil) {
        System.out.println("customer");
        FilterRegistrationBean<ClientFilter> filterRegistrationBean = new FilterRegistrationBean<>();
        filterRegistrationBean.setFilter(new ClientFilter(jwtUtil,ClientType.Customer));
        filterRegistrationBean.setOrder(2);
        filterRegistrationBean.addUrlPatterns("/customer/*");
        return filterRegistrationBean;
    }
}

通用ClientFilter实现

public class ClientFilter implements Filter {

    private JwtUtil jwtUtil;
    private ClientType client;

    public ClientFilter(JwtUtil jwtUtil, ClientType client) {
        this.jwtUtil = jwtUtil;
        this.client = client;
    }

    @Override
    public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
            throws IOException, ServletException {

        final HttpServletRequest request = (HttpServletRequest) req;
        final HttpServletResponse response = (HttpServletResponse) res;
        final String token = request.getHeader("token");
        System.out.println(request.getRequestURI());
        System.out.println(jwtUtil.extractClientType(token)+"   ---  "+this.client);
        try {
            if (token != null) {
                if (jwtUtil.extractClientType(token).equals(this.client)) {
                    chain.doFilter(request, response);
                } else {
                    response.sendError(HttpStatus.UNAUTHORIZED.value(), "you are not logged in");
                }
            } else {
                response.sendError(HttpStatus.UNAUTHORIZED.value(), "no token received");
            }
        } catch (Exception e) {
            response.sendError(HttpStatus.UNAUTHORIZED.value(), e.getMessage());

        }
    }
}

控制器示例(CompanyController)

@RestController
@RequestMapping("/company")
@CrossOrigin
public class CompanyController {

    @Autowired
    private CompanyService companyService;
    @Autowired
    private JwtUtil jwtUtil;
    @PostMapping("/coupon")
    public Coupon addCoupon(@RequestBody Coupon coupon, @RequestHeader String token) {

        try {
            int id=jwtUtil.extractId(token);
            return this.companyService.addCoupon(coupon,id);
        } catch (CouponSystemException e) {
            throw new ResponseStatusException(HttpStatus.BAD_REQUEST, e.getMessage());
        }
    }

    @PutMapping("/coupon")
    public Coupon updateCoupon(@RequestBody Coupon coupon, @RequestHeader String token) {
        
        try {
            int id=jwtUtil.extractId(token);
            return this.companyService.updateCoupon(coupon,id);
        } catch (CouponSystemException e) {
            throw new ResponseStatusException(HttpStatus.BAD_REQUEST, e.getMessage());
        }

    }
}

跨域Filter配置

@Component
public class AppCorsConfiguration {
    @Bean
    public FilterRegistrationBean<CorsFilter> corsFilter() {
        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        CorsConfiguration config = new CorsConfiguration();
        config.setAllowCredentials(true);
        config.addAllowedOrigin("http://localhost:3000");
        config.addAllowedHeader("*");
        config.addAllowedOriginPattern("*");
        config.addAllowedMethod("*");
        source.registerCorsConfiguration("/**", config);
        FilterRegistrationBean<CorsFilter> bean = new FilterRegistrationBean<CorsFilter>(new CorsFilter(source));
        bean.setOrder(0);
        return bean;
    }
}

问题分析与解决方案

核心问题排查

  1. 过滤器Order冲突:CorsFilter和companyFilter的Order都设为0,Spring中相同Order的过滤器执行顺序不确定,可能干扰后续过滤器的执行;同时需确保CorsFilter是第一个执行的过滤器,否则跨域请求会被提前拦截。
  2. 过滤器注册验证:启动项目时查看控制台是否打印company、admin、customer三个日志,若未打印,说明CompanyFilterComponent所在包不在Spring组件扫描范围内,需调整@ComponentScan配置。

具体修复步骤

  1. 调整过滤器执行顺序
    修改CorsFilter的Order为-1(确保全局跨域过滤器最先执行),保持三个客户端过滤器的Order递增:

    // AppCorsConfiguration中的corsFilter方法
    bean.setOrder(-1); // 改为-1,优先于所有业务过滤器执行
    
    // CompanyFilterComponent中的三个过滤器
    filterRegistrationBean.setOrder(0); // companyFilter
    filterRegistrationBean.setOrder(1); // adminFilter
    filterRegistrationBean.setOrder(2); // customerFilter
    
  2. 移除控制器上的@CrossOrigin注解
    全局CorsFilter已配置跨域规则,控制器上的@CrossOrigin会导致规则冲突,直接删除即可:

    @RestController
    @RequestMapping("/company")
    // 移除@CrossOrigin
    public class CompanyController {
        // ... 原有代码
    }
    
  3. 验证路径匹配与过滤器触发

    • 确认AdminController和CustomerController的@RequestMapping路径分别为/admin和/customer,确保Swagger请求路径与过滤器的addUrlPatterns规则匹配(如/admin/coupon会匹配/admin/*)。
    • 在ClientFilter的doFilter方法中添加日志,确认请求进入对应过滤器:
      System.out.println("当前过滤器类型:" + this.client + ",请求路径:" + request.getRequestURI());
      
  4. Token验证逻辑优化
    每个过滤器只会处理对应路径的请求,可简化逻辑避免不必要判断:

    @Override
    public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
            throws IOException, ServletException {
    
        final HttpServletRequest request = (HttpServletRequest) req;
        final HttpServletResponse response = (HttpServletResponse) res;
        final String token = request.getHeader("token");
        try {
            if (token == null) {
                response.sendError(HttpStatus.UNAUTHORIZED.value(), "no token received");
                return;
            }
            ClientType tokenClient = jwtUtil.extractClientType(token);
            if (!tokenClient.equals(this.client)) {
                response.sendError(HttpStatus.FORBIDDEN.value(), "当前客户端无权访问该路径");
                return;
            }
            chain.doFilter(request, response);
        } catch (Exception e) {
            response.sendError(HttpStatus.UNAUTHORIZED.value(), e.getMessage());
        }
    }
    

内容的提问来源于stack exchange,提问作者omer zvulun

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.16 15:50:39