# Gateway 认证方案说明 ## 问题背景 在微服务架构中,如果同时使用 Gateway 和 common-auth 模块,会出现**重复认证**的问题: ``` 浏览器 → Gateway (AuthGlobalFilter 验证 JWT) → 微服务 (JwtAuthenticationFilter 再次验证 JWT) ❌ 重复! ``` ## 解决方案 提供两种认证模式,通过配置选择: ### **模式一:Gateway 统一认证(推荐)** Gateway 负责认证,微服务信任 Gateway 传递的用户信息。 #### 架构流程 ``` 浏览器 ↓ (带 JWT Token) Nginx (80端口) ↓ Gateway (8080端口) ├─ AuthGlobalFilter: 验证 JWT ✓ ├─ 提取用户信息 (userId, username) ├─ 添加到请求头传递给下游 └─ 路由到微服务 ↓ 微服务 └─ GatewayTrustFilter: 从请求头获取用户信息 ✓ ``` #### 配置方式 **1. Gateway 服务配置** (`gateway/application.yml`) ```yaml auth: enabled: true token-header: Authorization token-prefix: "Bearer " auth-paths: - /auth/login - /auth/logout whitelist: - /actuator/** - /v3/api-docs/** ``` **2. 微服务配置** (`system/application.yml`, `log/application.yml` 等) ```yaml auth: enabled: true gateway-mode: true # 关键:启用 Gateway 模式 ``` #### 优点 - ✅ 避免重复认证,性能更好 - ✅ 统一认证逻辑,易维护 - ✅ 微服务之间调用不需要传递 JWT --- ### **模式二:微服务独立认证** 每个微服务独立验证 JWT,Gateway 不做认证。 #### 架构流程 ``` 浏览器 ↓ (带 JWT Token) Nginx ↓ Gateway └─ 直接路由(不验证) ↓ 微服务 └─ JwtAuthenticationFilter: 验证 JWT ✓ ``` #### 配置方式 **1. Gateway 服务配置** ```yaml auth: enabled: false # 关键:关闭 Gateway 认证 ``` **2. 微服务配置** ```yaml auth: enabled: true gateway-mode: false # 或不配置(默认 false) token-header: Authorization token-prefix: "Bearer " ``` #### 适用场景 - 微服务需要直接对外暴露(不经过 Gateway) - 对安全性要求极高,需要每层都验证 --- ## 配置文件对比 ### Gateway 服务 (`gateway/application.yml`) ```yaml server: port: 8080 spring: application: name: gateway-service cloud: gateway: routes: - id: auth-service uri: lb://auth-service predicates: - Path=/auth/** filters: - StripPrefix=1 - id: system-service uri: lb://system-service predicates: - Path=/system/** filters: - StripPrefix=1 # 认证配置 auth: enabled: true # 是否启用认证 gateway-mode: false # Gateway 本身不需要此配置 token-header: Authorization token-prefix: "Bearer " auth-paths: - /auth/login - /auth/logout - /auth/captcha - /auth/refresh whitelist: - /actuator/** - /v3/api-docs/** ``` ### 微服务配置 (Gateway 模式) **auth-service/application.yml** ```yaml server: port: 8081 spring: application: name: auth-service # 认证配置 auth: enabled: true gateway-mode: true # 关键:信任 Gateway token-header: Authorization token-prefix: "Bearer " whitelist: - /v3/api-docs/** - /actuator/** ``` **system-service/application.yml** ```yaml server: port: 8082 spring: application: name: system-service auth: enabled: true gateway-mode: true # 关键:信任 Gateway ``` --- ## 工作原理 ### Gateway 认证流程 **AuthGlobalFilter (Gateway 层)** ```java 1. 检查请求路径是否在白名单 2. 提取 Authorization 请求头中的 JWT Token 3. 验证 Token 是否过期 4. 验证 Token 签名是否有效 5. 提取用户信息 (userId, username) 6. 将用户信息添加到请求头: - X-User-Id: {userId} - X-Username: {username} 7. 路由到下游微服务 ``` ### 微服务信任流程 **GatewayTrustFilter (微服务层)** ```java 1. 从请求头获取 Gateway 传递的用户信息: - X-User-Id - X-Username 2. 构造 Spring Security 认证对象 3. 设置到 SecurityContext 4. 设置到 request attributes(供业务代码使用) ``` --- ## 安全考虑 ### 内网安全 采用 Gateway 模式时,需确保: 1. **微服务不对外暴露** - 只能通过 Gateway 访问 - 使用 Kubernetes Network Policy 或防火墙隔离 2. **请求头保护** - Gateway 在转发前清除任何客户端传递的 `X-User-Id` 等头 - 防止伪造用户身份 3. **Gateway 过滤器增强**(可选) ```java // 在 Gateway 中清除客户端可能伪造的请求头 ServerHttpRequest mutatedRequest = request.mutate() .headers(headers -> { headers.remove("X-User-Id"); headers.remove("X-Username"); }) .header(AuthContants.USER_ID_ATTRIBUTE, userId) .header(AuthContants.TOKEN_ATTRIBUTE, token) .build(); ``` --- ## 测试验证 ### 测试 Gateway 模式 **1. 登录获取 Token** ```bash curl -X POST http://localhost/api/auth/login \ -H "Content-Type: application/json" \ -d '{"username":"admin","password":"123456"}' # 响应 { "code": 200, "data": { "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...", "userId": "1001" } } ``` **2. 使用 Token 访问受保护接口** ```bash curl -X GET http://localhost/api/system/user/profile \ -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..." ``` **3. 查看日志验证单次认证** ``` Gateway 日志: [Gateway] Token 验证成功: userId=1001, path=/system/user/profile System-Service 日志: [GatewayTrustFilter] 从 Gateway 获取用户信息: userId=1001, username=admin ``` --- ## 迁移指南 ### 从独立认证迁移到 Gateway 统一认证 **步骤 1**: 更新所有微服务配置 ```yaml auth: gateway-mode: true # 添加这一行 ``` **步骤 2**: 重启服务(先重启微服务,后重启 Gateway) ```bash # 重启微服务 docker-compose restart auth-service system-service log-service # 重启 Gateway docker-compose restart gateway ``` **步骤 3**: 验证功能 - 测试登录 - 测试受保护接口访问 - 检查日志确认只认证一次 --- ## 常见问题 ### Q1: Gateway 模式下,微服务之间如何调用? **A**: 微服务间调用不需要传递 JWT Token,直接调用即可。Gateway已经验证过身份。 ```java // 微服务 A 调用微服务 B @Autowired private RestTemplate restTemplate; public void callServiceB() { // 直接调用,不需要添加 Authorization 头 String result = restTemplate.getForObject( "http://service-b/api/xxx", String.class ); } ``` ### Q2: 如何获取当前登录用户信息? **A**: 使用 `@HttpLogin` 注解或从 SecurityContext 获取。 ```java // 方式一:使用注解(推荐) @GetMapping("/profile") public ResultDomain getProfile(@HttpLogin LoginDomain loginDomain) { String userId = loginDomain.getUser().getUserId(); // ... } // 方式二:从 SecurityContext 获取 Authentication auth = SecurityContextHolder.getContext().getAuthentication(); String userId = (String) auth.getPrincipal(); ``` ### Q3: Gateway 模式更安全还是独立认证更安全? **A**: 取决于网络拓扑: - **内网隔离良好**: Gateway 模式更优(性能好,维护简单) - **微服务直接对外**: 独立认证更安全(每层验证) --- ## 推荐配置 ### 生产环境推荐 ```yaml # Gateway auth: enabled: true gateway-mode: false # 所有微服务 auth: enabled: true gateway-mode: true ``` ### 开发环境(快速调试) 可以临时关闭认证: ```yaml auth: enabled: false ``` --- ## 总结 | 对比项 | Gateway 统一认证 | 微服务独立认证 | |--------|-----------------|---------------| | 认证次数 | 1次(仅 Gateway) | N次(每个服务) | | 性能 | ⭐⭐⭐⭐⭐ 最优 | ⭐⭐⭐ 一般 | | 维护性 | ⭐⭐⭐⭐⭐ 统一管理 | ⭐⭐⭐ 分散管理 | | 安全性 | ⭐⭐⭐⭐ 需内网隔离 | ⭐⭐⭐⭐⭐ 多层防护 | | 推荐场景 | 内网微服务架构 | 微服务对外暴露 | **推荐使用 Gateway 统一认证模式!**