Spring Cloud Gateway 集成 Redis 实现分布式会话鉴权怎么配置?

文章导读
Spring Cloud Gateway 本身不提供开箱即用的会话鉴权开关,通常需结合 Spring Session 与 Redis,并通过自定义 GlobalFilter 实现,适合需要维持用户登录状态的中心化网关场景。
📋 目录
  1. 核心依赖引入
  2. 完整配置示例 (application.yml)
  3. Redis 会话序列化配置
  4. 鉴权过滤器代码实现
  5. 跨域与 Cookie 配置详解
  6. 怎么验证是否生效
  7. 常见坑
  8. 参考来源
A A

Spring Cloud Gateway 本身不提供开箱即用的会话鉴权开关,通常需结合 Spring Session 与 Redis,并通过自定义 GlobalFilter 实现,适合需要维持用户登录状态的中心化网关场景。

先说结论:该方案适合有状态认证需求的网关层,需先准备 Redis 环境与响应式依赖,验收时重点检查 Cookie 传递与 Redis 键值。

  • 适合:需要在网关层统一拦截并校验登录状态的业务场景
  • 先准备:确保 Redis 服务可用,引入 spring-session-data-redis 与响应式栈依赖
  • 验收:通过浏览器请求携带 Cookie,确认 Redis 中存在会话键且网关放行

核心依赖引入

在 pom.xml 中引入 Redis 响应式客户端与 Spring Session 依赖,注意不要混入 Servlet 栈依赖(如 spring-boot-starter-web),否则会导致启动冲突。

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis-reactive</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.session</groupId>
    <artifactId>spring-session-data-redis</artifactId>
</dependency>

完整配置示例 (application.yml)

配置 Redis 连接信息、会话存储类型以及 Cookie 的跨域属性。确保 `spring.session.store-type` 设置为 `redis`。

server:
  port: 8080
  cookie:
    same-site: none
    secure: true
spring:
  redis:
    host: localhost
    port: 6379
    password: your_password
  session:
    store-type: redis
    redis: 
      namespace: spring:session
    timeout: 30m

Redis 会话序列化配置

为避免网关与认证服务端的会话数据无法读取,需统一序列化方式。推荐使用 JSON 序列化而非 JDK 默认序列化,以便跨语言或跨版本兼容。

Spring Cloud Gateway 集成 Redis 实现分布式会话鉴权怎么配置?
@Configuration
public class RedisConfig {
    @Bean
    public RedisSerializer<Object> springSessionDefaultRedisSerializer() {
        return new GenericJackson2JsonRedisSerializer();
    }

    @Bean
    public ReactiveRedisOperations<String, Object> reactiveRedisOperations(RedisConnectionFactory factory) {
        RedisSerializationContext<String, Object> context = RedisSerializationContext.newSerializationContext()
                .key(new StringRedisSerializer())
                .value(new GenericJackson2JsonRedisSerializer())
                .build();
        return new ReactiveRedisTemplate<>(factory, context);
    }
}

鉴权过滤器代码实现

创建实现 GlobalFilter 和 Ordered 接口的类。在 filter 方法中从 ServerWebExchange 获取 Cookie,通过 ReactiveRedisOperations 查询 Redis 会话仓库,避免阻塞 IO。

@Component
public class AuthFilter implements GlobalFilter, Ordered {

    @Autowired
    private ReactiveRedisOperations<String, Object> redisOperations;

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        ServerHttpRequest request = exchange.getRequest();
        String sessionId = extractSessionId(request);

        if (sessionId == null) {
            exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
            return exchange.getResponse().setComplete();
        }

        String sessionKey = "spring:session:sessions:" + sessionId;
        return redisOperations.hasKey(sessionKey)
                .flatMap(exists -> {
                    if (Boolean.TRUE.equals(exists)) {
                        return chain.filter(exchange);
                    } else {
                        exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
                        return exchange.getResponse().setComplete();
                    }
                });
    }

    private String extractSessionId(ServerHttpRequest request) {
        HttpCookie cookie = request.getCookies().getFirst("SESSION");
        return cookie != null ? cookie.getValue() : null;
    }

    @Override
    public int getOrder() {
        return -100;
    }
}

跨域与 Cookie 配置详解

若前端与网关域名不同,需在网关配置中允许 Cookie 跨域携带。除了 YAML 配置外,还需在 Cors 配置中设置 `allowCredentials(true)`。

@Bean
public CorsWebFilter corsWebFilter() {
    CorsConfiguration config = new CorsConfiguration();
    config.setAllowCredentials(true);
    config.addAllowedOriginPattern("*");
    config.addAllowedHeader("*");
    config.addAllowedMethod("*");
    UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
    source.registerCorsConfiguration("/**", config);
    return new CorsWebFilter(source);
}

怎么验证是否生效

1. 查看 Redis:使用 `redis-cli keys "spring:session:*"` 命令,登录后应能看到生成的会话键。

2. 检查请求:浏览器发起请求时,确认 Request Headers 中包含 Cookie 字段,且值为 SESSION=xxx。

Spring Cloud Gateway 集成 Redis 实现分布式会话鉴权怎么配置?

3. 观察响应:未登录请求应返回 401 Unauthorized,登录后的请求应正常转发至微服务并返回 200。

4. 日志确认:在网关日志中打印过滤器拦截日志,确认会话加载成功或失败的具体原因。

常见坑

1. Cookie 域名不匹配:网关域名与前端页面域名不一致时,浏览器可能拒绝发送 Cookie,需配置 same-site 为 None 且开启 secure。

2. 依赖冲突:引入 spring-session-data-redis 时若不小心带入 spring-boot-starter-web,会导致启动报错,因为 Gateway 不支持 Servlet 容器。

Spring Cloud Gateway 集成 Redis 实现分布式会话鉴权怎么配置?

3. 序列化问题:Redis 中存储的会话对象若自定义了属性,需确保网关与认证服务端的序列化配置一致,否则无法读取。

4. 性能损耗:每次请求都访问 Redis 会增加网络开销,高并发场景下建议监控 Redis 延迟,必要时引入本地缓存减轻压力。

参考来源

1. Spring Cloud Gateway 官方项目页,Spring Cloud Gateway,https://spring.io/projects/spring-cloud-gateway

2. Spring Session 官方项目页,Spring Session,https://spring.io/projects/spring-session