Gateway网关拦截器与Redis:打造高效单点登录与认证系统
引言
在微服务架构中,Spring Cloud Gateway(以下简称 Gateway)常被用作系统的统一入口,负责路由、限流、监控等功能。与此同时,单点登录(SSO)与认证是保障系统安全、提升用户体验的关键。结合Redis的高性能特性,利用 Gateway 的拦截器(Filter)实现统一鉴权与会话管理,能够打造一套高效、可伸缩的单点登录与认证系统。
本文将从架构设计、核心原理、代码示例与图解四个方面,详细剖析 Gateway 拦截器 + Redis 方案,帮助你快速上手并轻松学习。
一、架构设计
┌──────────┐ ┌──────────┐ ┌────────────┐
│ 用户浏览器 │ ──→ │ Spring │ ──→ │ 后端微服务1 │
│ (携带Token)│ │ Cloud │ └────────────┘
└──────────┘ │ Gateway │ ┌────────────┐
└───┬──────┘ ──→ │ 后端微服务2 │
│ └────────────┘
┌──────────────┐ │
│ Redis │◀┘
│ (Session Store)│
└──────────────┘
- 用户浏览器:在登录后携带 JWT/Token 访问各微服务。
- Gateway:接收请求后,通过拦截器校验 Token,并查询 Redis 获取会话或权限信息,决定放行或拒绝。
- Redis:存储 Token 与用户会话数据,支持高并发读写,保障鉴权极低延迟。
- 微服务:只需关注业务逻辑,无需重复实现鉴权逻辑。
二、核心原理
Token 签发与存储
- 用户登录成功后,认证服务生成 JWT 并同时在 Redis 中存储会话(或权限列表),Key 为
SESSION:{token}
,Value 为用户信息 JSON。
- 用户登录成功后,认证服务生成 JWT 并同时在 Redis 中存储会话(或权限列表),Key 为
Gateway 拦截器
- 每次请求到达 Gateway 时,Filter 先从 HTTP Header(如
Authorization: Bearer <token>
)中提取 Token; - 去 Redis 校验 Token 是否有效,并可选地加载用户权限;
- 校验通过则将用户信息注入 Header 或上下文,转发给下游微服务;否则返回
401 Unauthorized
。
- 每次请求到达 Gateway 时,Filter 先从 HTTP Header(如
Redis 会话管理
- 设置过期时间(如 30 分钟),实现自动失效;
- 支持单点登出:从 Redis 删除会话,立即使所有网关拦截器失效。
三、代码示例
1. Redis 配置
@Configuration
public class RedisConfig {
@Bean
public JedisConnectionFactory jedisConnectionFactory() {
RedisStandaloneConfiguration cfg = new RedisStandaloneConfiguration("localhost", 6379);
return new JedisConnectionFactory(cfg);
}
@Bean
public RedisTemplate<String, Object> redisTemplate(JedisConnectionFactory factory) {
RedisTemplate<String, Object> template = new RedisTemplate<>();
template.setConnectionFactory(factory);
template.setKeySerializer(new StringRedisSerializer());
template.setValueSerializer(new GenericJackson2JsonRedisSerializer());
return template;
}
}
2. 认证服务:Token 签发与存储
@RestController
@RequestMapping("/auth")
public class AuthController {
@Autowired private RedisTemplate<String,Object> redisTemplate;
@PostMapping("/login")
public ResponseEntity<?> login(@RequestBody LoginDTO dto) {
// 验证用户名密码略…
String token = JwtUtil.generateToken(dto.getUsername());
// 存入 Redis,设置 30 分钟过期
String key = "SESSION:" + token;
UserInfo userInfo = new UserInfo(dto.getUsername(), List.of("ROLE_USER"));
redisTemplate.opsForValue().set(key, userInfo, 30, TimeUnit.MINUTES);
return ResponseEntity.ok(Map.of("token", token));
}
@PostMapping("/logout")
public ResponseEntity<?> logout(@RequestHeader("Authorization") String auth) {
String token = auth.replace("Bearer ", "");
redisTemplate.delete("SESSION:" + token);
return ResponseEntity.ok().build();
}
}
3. Gateway 拦截器实现
@Component
public class AuthGlobalFilter implements GlobalFilter, Ordered {
@Autowired private RedisTemplate<String,Object> redisTemplate;
@Override
public int getOrder() {
return -1; // 优先级高于路由转发
}
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
// 1. 提取 Token
String auth = exchange.getRequest().getHeaders().getFirst("Authorization");
if (auth == null || !auth.startsWith("Bearer ")) {
return unauthorized(exchange);
}
String token = auth.replace("Bearer ", "");
// 2. Redis 校验
String key = "SESSION:" + token;
Object userInfo = redisTemplate.opsForValue().get(key);
if (userInfo == null) {
return unauthorized(exchange);
}
// 3. 延长会话有效期
redisTemplate.expire(key, 30, TimeUnit.MINUTES);
// 4. 将用户信息放入 Header,透传给下游
exchange = exchange.mutate()
.request(r -> r.header("X-User-Info", JsonUtils.toJson(userInfo)))
.build();
return chain.filter(exchange);
}
private Mono<Void> unauthorized(ServerWebExchange exchange) {
exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
DataBuffer buffer = exchange.getResponse().bufferFactory()
.wrap("{\"error\":\"Unauthorized\"}".getBytes());
return exchange.getResponse().writeWith(Mono.just(buffer));
}
}
四、图解流程
┌─────────────┐ 1. 登录请求 ┌──────────────┐
│ 用户浏览器 │ ──→ /auth/login ──→ │ 认证服务(Auth) │
└─────────────┘ └──────────────┘
│
2. 签发 JWT & 存 Redis(key=SESSION:token, value=UserInfo)
▼
┌─────────────┐ 3. 携带 Token ┌──────────┐
│ 用户浏览器 │ ──→ 接入请求 ──→ │ Gateway │
└─────────────┘ └────┬─────┘
│
4. 校验 Redis(key=SESSION:token)
│
┌────────────┴────────────┐
│ │
有效 → 延长过期 & 注入用户信息 无效 → 返回 401
│
▼
5. 转发到后端微服务
五、详细说明
全局 Filter vs 路由 Filter
- 本示例使用
GlobalFilter
,对所有路由生效; - 若需针对特定路由,可改用
GatewayFilterFactory
定制化 Filter。
- 本示例使用
会话延迟策略
- 每次请求命中后主动延长 Redis Key 过期时间,实现“滑动过期”;
- 可根据业务调整为固定过期或多级过期。
多实例部署与高可用
- Gateway 与认证服务可水平扩展;
- Redis 可部署哨兵或集群模式,保证高可用和容灾。
安全加固
- 建议在 JWT 中添加签名与加密;
- 对敏感 Header 与 Cookie 做安全校验;
- 考虑使用 HTTPS,防止中间人攻击。
六、总结
通过上述方案,你可以快速构建基于 Spring Cloud Gateway + Redis 的单点登录与认证系统:
- 高性能:Redis 提供毫秒级读写;
- 高可用:组件可独立扩展与集群化部署;
- 易维护:认证逻辑集中在 Gateway,一处修改全局生效。
评论已关闭