# 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<UserDTO> 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 统一认证模式！**
