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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.26 11:52:37