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; } }
问题分析与解决方案
核心问题排查
- 过滤器Order冲突:CorsFilter和companyFilter的
Order都设为0,Spring中相同Order的过滤器执行顺序不确定,可能干扰后续过滤器的执行;同时需确保CorsFilter是第一个执行的过滤器,否则跨域请求会被提前拦截。 - 过滤器注册验证:启动项目时查看控制台是否打印
company、admin、customer三个日志,若未打印,说明CompanyFilterComponent所在包不在Spring组件扫描范围内,需调整@ComponentScan配置。
具体修复步骤
调整过滤器执行顺序
修改CorsFilter的Order为-1(确保全局跨域过滤器最先执行),保持三个客户端过滤器的Order递增:// AppCorsConfiguration中的corsFilter方法 bean.setOrder(-1); // 改为-1,优先于所有业务过滤器执行// CompanyFilterComponent中的三个过滤器 filterRegistrationBean.setOrder(0); // companyFilter filterRegistrationBean.setOrder(1); // adminFilter filterRegistrationBean.setOrder(2); // customerFilter移除控制器上的@CrossOrigin注解
全局CorsFilter已配置跨域规则,控制器上的@CrossOrigin会导致规则冲突,直接删除即可:@RestController @RequestMapping("/company") // 移除@CrossOrigin public class CompanyController { // ... 原有代码 }验证路径匹配与过滤器触发
- 确认AdminController和CustomerController的
@RequestMapping路径分别为/admin和/customer,确保Swagger请求路径与过滤器的addUrlPatterns规则匹配(如/admin/coupon会匹配/admin/*)。 - 在
ClientFilter的doFilter方法中添加日志,确认请求进入对应过滤器:System.out.println("当前过滤器类型:" + this.client + ",请求路径:" + request.getRequestURI());
- 确认AdminController和CustomerController的
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
相关产品推荐
相关产品推荐

