Files
urbanLifeline/docs/网关认证方案.md
2025-12-02 18:46:03 +08:00

8.1 KiB
Raw Blame History

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)

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 等)

auth:
  enabled: true
  gateway-mode: true  # 关键:启用 Gateway 模式

优点

  • 避免重复认证,性能更好
  • 统一认证逻辑,易维护
  • 微服务之间调用不需要传递 JWT

模式二:微服务独立认证

每个微服务独立验证 JWTGateway 不做认证。

架构流程

浏览器 
  ↓ (带 JWT Token)
Nginx
  ↓
Gateway
  └─ 直接路由(不验证)
       ↓
微服务
  └─ JwtAuthenticationFilter: 验证 JWT ✓

配置方式

1. Gateway 服务配置

auth:
  enabled: false  # 关键:关闭 Gateway 认证

2. 微服务配置

auth:
  enabled: true
  gateway-mode: false  # 或不配置(默认 false
  token-header: Authorization
  token-prefix: "Bearer "

适用场景

  • 微服务需要直接对外暴露(不经过 Gateway
  • 对安全性要求极高,需要每层都验证

配置文件对比

Gateway 服务 (gateway/application.yml)

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

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

server:
  port: 8082

spring:
  application:
  name: system-service

auth:
  enabled: true
  gateway-mode: true         # 关键:信任 Gateway

工作原理

Gateway 认证流程

AuthGlobalFilter (Gateway 层)

1. 检查请求路径是否在白名单
2. 提取 Authorization 请求头中的 JWT Token
3. 验证 Token 是否过期
4. 验证 Token 签名是否有效
5. 提取用户信息 (userId, username)
6. 将用户信息添加到请求头
   - X-User-Id: {userId}
   - X-Username: {username}
7. 路由到下游微服务

微服务信任流程

GatewayTrustFilter (微服务层)

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 过滤器增强(可选)

    // 在 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

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 访问受保护接口

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: 更新所有微服务配置

auth:
  gateway-mode: true  # 添加这一行

步骤 2: 重启服务(先重启微服务,后重启 Gateway

# 重启微服务
docker-compose restart auth-service system-service log-service

# 重启 Gateway
docker-compose restart gateway

步骤 3: 验证功能

  • 测试登录
  • 测试受保护接口访问
  • 检查日志确认只认证一次

常见问题

Q1: Gateway 模式下,微服务之间如何调用?

A: 微服务间调用不需要传递 JWT Token直接调用即可。Gateway已经验证过身份。

// 微服务 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 获取。

// 方式一:使用注解(推荐)
@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 模式更优(性能好,维护简单)
  • 微服务直接对外: 独立认证更安全(每层验证)

推荐配置

生产环境推荐

# Gateway
auth:
  enabled: true
  gateway-mode: false

# 所有微服务
auth:
  enabled: true
  gateway-mode: true

开发环境(快速调试)

可以临时关闭认证:

auth:
  enabled: false

总结

对比项 Gateway 统一认证 微服务独立认证
认证次数 1次仅 Gateway N次每个服务
性能 最优 一般
维护性 统一管理 分散管理
安全性 需内网隔离 多层防护
推荐场景 内网微服务架构 微服务对外暴露

推荐使用 Gateway 统一认证模式!