Spring Cloud Gateway全局过滤器优先级问题:认证前置执行
问题:Spring Cloud Gateway 过滤器执行顺序不符合预期
现有架构与配置
- 三个微服务:A、B(OAuth2资源服务)、C(Spring Cloud Gateway,作为OAuth2客户端)
微服务A/B的Security配置
@Bean public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { return http .csrf(csrf -> csrf.disable()) .cors(Customizer.withDefaults()) .headers(h -> h.frameOptions(fo -> fo.disable())) .authorizeHttpRequests(ar-> ar.anyRequest().authenticated()) .oauth2ResourceServer(o2rs -> o2rs.jwt(jwt -> jwt.jwtAuthenticationConverter(jwtAuthConverter))) .build(); }
网关C的配置文件
spring: application.name: backend-gateway-client cloud: gateway: default-filters: - TokenRelay routes: - id: resources uri: http://localhost:8082/messages predicates: Path=/messages/** - id: customer uri: http://localhost:8086/ predicates: Path=/customers/** security: oauth2: client: registration: gateway: provider: my-provider client-id: front-end-angular-client client-secret: xWskwS0avQaf1rLOwqdUDhROopWoOjWl authorization-grant-type: authorization_code redirect-uri: "http://localhost:8083/login/oauth2/code/{registrationId}" scope: openid provider: my-provider: issuer-uri: http://localhost:8081/realms/ecom-web-app
网关C的全局过滤器
@Component @Order(Ordered.HIGHEST_PRECEDENCE) public class CustomGlobalFilter implements GlobalFilter { @Override public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) { // 从请求头获取token String token = exchange.getRequest().getHeaders().getFirst("Authorization"); if (isAuthenticated(exchange.getRequest())) { // 已认证,放行请求 return chain.filter(exchange); } else { exchange.getResponse().setStatusCode(HttpStatus.SEE_OTHER); exchange.getResponse().getHeaders().set(HttpHeaders.LOCATION, "http://localhost:4200"); return exchange.getResponse().setComplete(); } } }
核心问题
期望执行顺序:请求→全局过滤器校验token→决定是否触发认证
实际执行顺序:请求→OAuth2认证流程→全局过滤器
解决思路
1. 调整过滤器优先级,让自定义过滤器先于Spring Security执行
Spring Cloud Gateway中,Spring Security的过滤器链默认优先级高于自定义全局过滤器,即使设置@Order(Ordered.HIGHEST_PRECEDENCE)也无法覆盖。可以改用WebFilter并指定更低的order值(Spring Security过滤器默认order为-100,设置为-101即可):
@Component @Order(-101) public class CustomAuthCheckFilter implements WebFilter { @Override public Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) { // 原有的token校验逻辑 String token = exchange.getRequest().getHeaders().getFirst("Authorization"); if (isAuthenticated(exchange.getRequest())) { return chain.filter(exchange); } else { exchange.getResponse().setStatusCode(HttpStatus.SEE_OTHER); exchange.getResponse().getHeaders().set(HttpHeaders.LOCATION, "http://localhost:4200"); return exchange.getResponse().setComplete(); } } }
2. 整合到Spring Security配置中(推荐)
直接在网关的Security配置类中实现校验逻辑,替代独立的全局过滤器,更好地适配Spring Security的执行流程:
@Configuration public class GatewaySecurityConfig { @Bean public SecurityFilterChain securityFilterChain(ServerHttpSecurity http) throws Exception { http .csrf(csrf -> csrf.disable()) .authorizeExchange(exchanges -> exchanges // 先执行自定义认证校验 .anyExchange().access(this::customAuthCheck) ) .oauth2Login(oauth2 -> oauth2 // 校验不通过时跳转到前端登录页 .loginPage("http://localhost:4200") ); return http.build(); } private Mono<AuthorizationDecision> customAuthCheck(AuthorizationContext context) { ServerHttpRequest request = context.getExchange().getRequest(); String token = request.getHeaders().getFirst("Authorization"); // 实现真实的token有效性校验逻辑: // 1. 检查token格式是否为Bearer xxx // 2. 调用授权服务器introspect接口验证,或本地解析JWT验证签名/过期时间 boolean isAuthenticated = checkTokenValidity(token); return Mono.just(new AuthorizationDecision(isAuthenticated)); } private boolean checkTokenValidity(String token) { if (token == null || !token.startsWith("Bearer ")) { return false; } String jwtToken = token.substring(7); // 示例:替换为实际校验逻辑(比如用JwtDecoder解析验证) // return jwtDecoder.decode(jwtToken) != null; return true; } }
3. 禁用默认Security自动配置(谨慎操作)
如果不需要网关自动处理OAuth2登录跳转,可以排除自动配置类,完全手动控制认证流程,但会丢失Spring Security的OAuth2客户端能力:
@SpringBootApplication(exclude = {SecurityAutoConfiguration.class, OAuth2ClientAutoConfiguration.class}) public class GatewayApplication { public static void main(String[] args) { SpringApplication.run(GatewayApplication.class, args); } }
内容的提问来源于stack exchange,提问作者malala rado
相关产品推荐
相关产品推荐

