Chuyển đến nội dung chính

第 22 課:API 閘道、Nginx 與微服務

Keycloak 與 Nginx(lua-resty-openidc/nginx-oidc-module 或 OAuth2 代理)、Kong Gateway(OIDC 外掛程式)、Traefik(ForwardAuth 中間件)、微服務 API 閘道模式、服務帳戶驗證(客戶端憑證授予)、代幣交換器、內部服務識別碼

🔒 DevSecOps — 第 22 課 第 22 課:API 閘道、Nginx 與 微服務

從基礎到進階的鑰匙斗篷__HTMLTAG_59___

第 6 部分:整合實際應用__HTMLTAG_62___

xdev.asia

HTMLTAG_67__HTMLTAG_68___1。帶有 Keycloak 的 API 閘道模式___HTMLTAG_69__HTMLTAG_70

在微服務架構中,API 閘道可作為所有客戶端請求的單一入口點。與 Keycloak 結合使用時,閘道可處理 集中式驗證 和 JWT 傳播 到下游服務。

預編碼_0

___HTMLTAG_79__HTMLTAG_80___網關___HTMLTAG_81__HTMLTAG_82___驗證方法___HTMLTAG_83__HTMLTAG_84___優點____HTMLTAG_85__HTMLTAG_86___ ___HTMLTAG_89__HTMLTAG_90___Nginx + lua-resty-openidc___HTMLTAG_91__HTMLTAG_92___Lua模組OIDC___HTMLTAG_93__HTMLTAG_94___輕量級、高效能___HTMLTAG95__1TAG_96___ ___HTMLTAG_97__HTMLTAG_98___Nginx + OAuth2 代理___HTMLTAG_99__HTMLTAG_100___Sidecar 代理___HTMLTAG_101__HTMLTAG_102___設定簡單,不需 Lua____HTMLTAG_101__HTMLTAG_102___設定簡單,不需 Lua____HTMLTAG_103__41TAG_103__4 ___HTMLTAG_105__HTMLTAG_106___Kong Gateway___HTMLTAG_107__HTMLTAG_108___OIDC插件____HTMLTAG_109__HTMLTAG_110___企業功能、插件生態系統____HTMLTAG_111__HTMLTAG_112___ ___HTMLTAG_113__HTMLTAG_114___Traefik____HTMLTAG_115__HTMLTAG_116___ForwardAuth___HTMLTAG_117__HTMLTAG_118___雲原生、自動發現___HTMLTAG_119__HTMLTAG_120___

HTMLTAG_123__HTMLTAG_124___2。 Nginx + Keycloak 整合___HTMLTAG_125__HTMLTAG_126

HTMLTAG_127__HTMLTAG_128___2.1 方法 1:lua-resty-openidc___HTMLTAG_129__HTMLTAG_130

___HTMLTAG_131__HTMLTAG_132___lua-resty-openidc 是 Nginx/OpenResty 的 OpenID Connect 模組,直接在 Nginx 層支援 JWT 驗證、令牌自省和 OIDC 登入流程。

2.1.1 安裝 OpenResty

預編碼_1

2.1.2 具有不記名令牌驗證的 Nginx 設定__HTMLTAG_138___

預編碼_2

HTMLTAG_139__HTMLTAG_140___2.2 方法 2:OAuth2 代理 Sidecar___HTMLTAG_141__HTMLTAG_142

___HTMLTAG_143__HTMLTAG_144___oauth2-proxy 是一個反向代理,透過 OAuth2/OIDC 提供者提供驗證。這種方法不需要 Lua 並且更容易設定:

預編碼_3

2.2.1 OAuth2 代理程式設定__HTMLTAG_148___

預編碼_4

2.2.2 Nginx 與 auth_request

預編碼_5

HTMLTAG_151__HTMLTAG_152___3。 Kong網關+鑰匙斗篷___HTMLTAG_153__HTMLTAG_154

HTMLTAG_155__HTMLTAG_156___3.1 Kong OIDC插件配置___HTMLTAG_157__HTMLTAG_158

Kong Gateway 支援 OIDC 外掛程式(Kong Enterprise 或社群外掛程式)來驗證來自 Keycloak 的 JWT 令牌:

預編碼_6

HTMLTAG_161__HTMLTAG_162___3.2 Kong 管理 API 設定___HTMLTAG_163__HTMLTAG_164

透過 Kong 管理 API 而非宣告式進行設定:

預編碼_7

HTMLTAG_167__HTMLTAG_168___4。 Traefik + Keycloak___HTMLTAG_169__HTMLTAG_170

HTMLTAG_171__HTMLTAG_172___4.1 ForwardAuth 中間件___HTMLTAG_173__HTMLTAG_174

Traefik 使用 ForwardAuth 中間件 將身分驗證委託給外部服務(OAuth2 代理):

預編碼_8

預編碼_9

HTMLTAG_179__HTMLTAG_180___4.2 帶有 Docker 標籤的 Traefik___HTMLTAG_181__HTMLTAG_182

預編碼_10

HTMLTAG_183__HTMLTAG_184___5。服務帳戶驗證___HTMLTAG_185__HTMLTAG_186

HTMLTAG_187__HTMLTAG_188___5.1 客戶憑證授予___HTMLTAG_189__HTMLTAG_190

對於服務到服務通訊(沒有使用者上下文),請使用 客戶端憑證授予:

┌──────────────┐                    ┌──────────────┐
│ Service A    │                    │ Keycloak     │
│ (Order)      │                    │              │
│              │── 1. client_id ───▶│              │
│              │   + client_secret  │              │
│              │                    │              │
│              │◀── 2. Access ──────│              │
│              │    Token           │              │
└──────┬───────┘                    └──────────────┘
       │
       │ 3. Bearer token
       ▼
┌──────────────┐
│ Service B    │
│ (Payment)    │
│              │
└──────────────┘

5.1.1 Keycloak 客戶端設定__HTMLTAG_196___
Client Settings cho Service Account:
  Client ID:           order-service
  Client Protocol:     openid-connect
  Access Type:         confidential
  Service Accounts:    ON
  Standard Flow:       OFF (không cần user login)
  Direct Access:       OFF

  Service Account Roles:
    → Assign realm roles hoặc client roles cần thiết
    → Ví dụ: payment-read, payment-write

5.1.2 取得服務帳號代幣

# Client Credentials Grant
curl -s -X POST \
  "http://localhost:8080/realms/my-realm/protocol/openid-connect/token" \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -d "grant_type=client_credentials" \
  -d "client_id=order-service" \
  -d "client_secret=order-service-secret" \
  | jq .

# Response:
# {
#   "access_token": "eyJhbGci...",
#   "expires_in": 300,
#   "token_type": "Bearer",
#   "not-before-policy": 0,
#   "scope": "profile email"
# }

5.1.3 Spring Boot 中的服務帳戶

package com.example.service;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.*;
import org.springframework.stereotype.Service;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.client.RestTemplate;

import java.time.Instant;
import java.util.Map;

@Service
public class ServiceAccountTokenProvider {

    @Value("${keycloak.auth-server-url}")
    private String keycloakUrl;

    @Value("${keycloak.realm}")
    private String realm;

    @Value("${keycloak.service-account.client-id}")
    private String clientId;

    @Value("${keycloak.service-account.client-secret}")
    private String clientSecret;

    private final RestTemplate restTemplate = new RestTemplate();

    private String cachedToken;
    private Instant tokenExpiry;

    public synchronized String getServiceAccountToken() {
        // Return cached token nếu còn hạn
        if (cachedToken != null && Instant.now().isBefore(tokenExpiry)) {
            return cachedToken;
        }

        // Request new token
        String tokenUrl = String.format(
            "%s/realms/%s/protocol/openid-connect/token",
            keycloakUrl, realm
        );

        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);

        MultiValueMap<String, String> body = new LinkedMultiValueMap<>();
        body.add("grant_type", "client_credentials");
        body.add("client_id", clientId);
        body.add("client_secret", clientSecret);

        HttpEntity<MultiValueMap<String, String>> request =
            new HttpEntity<>(body, headers);

        ResponseEntity<Map> response = restTemplate.postForEntity(
            tokenUrl, request, Map.class
        );

        Map<String, Object> tokenResponse = response.getBody();
        cachedToken = (String) tokenResponse.get("access_token");
        int expiresIn = (Integer) tokenResponse.get("expires_in");
        // Refresh 30 giây trước khi hết hạn
        tokenExpiry = Instant.now().plusSeconds(expiresIn - 30);

        return cachedToken;
    }
}
// Sử dụng service account token để gọi downstream service
@Service
public class PaymentServiceClient {

    private final RestTemplate restTemplate = new RestTemplate();
    private final ServiceAccountTokenProvider tokenProvider;

    public PaymentServiceClient(ServiceAccountTokenProvider tokenProvider) {
        this.tokenProvider = tokenProvider;
    }

    public PaymentResult processPayment(PaymentRequest request) {
        String token = tokenProvider.getServiceAccountToken();

        HttpHeaders headers = new HttpHeaders();
        headers.setBearerAuth(token);
        headers.setContentType(MediaType.APPLICATION_JSON);

        HttpEntity<PaymentRequest> entity = new HttpEntity<>(request, headers);

        ResponseEntity<PaymentResult> response = restTemplate.exchange(
            "http://payment-service:8083/api/payments",
            HttpMethod.POST,
            entity,
            PaymentResult.class
        );

        return response.getBody();
    }
}

___HTMLTAG_201__HTMLTAG_202___5.2 令牌交換 (RFC 8693)HTMLTAG_203__HTMLTAG_204

令牌交換允許服務將使用者令牌交換為具有不同範圍/受眾的新令牌,或在呼叫下游服務時模擬使用者:

# Bật Token Exchange trong Keycloak
# Realm Settings → Token → Token Exchange: Enable

# Exchange user token thành service-specific token
curl -s -X POST \
  "http://localhost:8080/realms/my-realm/protocol/openid-connect/token" \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -d "grant_type=urn:ietf:params:oauth:grant-type:token-exchange" \
  -d "client_id=order-service" \
  -d "client_secret=order-service-secret" \
  -d "subject_token=$USER_ACCESS_TOKEN" \
  -d "subject_token_type=urn:ietf:params:oauth:token-type:access_token" \
  -d "audience=payment-service" \
  -d "requested_token_type=urn:ietf:params:oauth:token-type:access_token" \
  | jq .

# Response chứa token mới với audience = payment-service
# Token vẫn giữ user identity nhưng scoped cho payment-service
Luồng Token Exchange:

User ──▶ Order Service (user token)
              │
              ├── Exchange user token → Keycloak
              │   (audience = payment-service)
              │
              ◀── New token (user context, scoped for payment)
              │
              ├── Call Payment Service (exchanged token)
              │
              ◀── Payment result

HTMLTAG_207__HTMLTAG_208___5.3 內部服務驗證模式___HTMLTAG_209__HTMLTAG_210

___HTMLTAG_213__HTMLTAG_214___模式____HTMLTAG_215__HTMLTAG_216___用例___HTMLTAG_217__HTMLTAG_218_______HTMLTAG_219__HTMLTAG_220________HTMLTAG2121_____ ___HTMLTAG_225__HTMLTAG_226___JWT 傳播___HTMLTAG_227__HTMLTAG_228___將使用者令牌轉送至下游___HTMLTAG_229__HTMLTAG_230___簡單,保留使用者上下文___HT ___HTMLTAG_235__HTMLTAG_236___客戶端憑證___HTMLTAG_237__HTMLTAG_238___服務到服務,無使用者上下文____HTMLTAG_239__HTMLTAG_240___獨立於使用者工作階段____HTMLTAG_241__4UG_241__41424UU42____ML41__4U424141____ ___HTMLTAG_245__HTMLTAG_246___令牌交換____HTMLTAG_247__HTMLTAG_248___模擬、受眾限制____HTMLTAG_249__HTMLTAG_250___使用者上下文+範圍存取____HTMLTAG_2511HTMLTAG_250___用戶上下文+範圍存取____HTMLTAG_2511HTMLTAG_250___ML ___HTMLTAG_255__HTMLTAG_256___mTLS___HTMLTAG_257__HTMLTAG_258___零信任服務網格___HTMLTAG_259__HTMLTAG_260___強身份,無需令牌____HTMLTAG_261_HTMLTAGML_262_______MLTAGML42_____

HTMLTAG_267__HTMLTAG_268___6。完整的 Docker Compose 堆疊___HTMLTAG_269__HTMLTAG_270

Docker Compose 堆疊配有 Keycloak、PostgreSQL、Nginx 閘道和後端服務:

# docker-compose.yml
version: '3.9'

services:
  # ===== PostgreSQL Database =====
  postgres:
    image: postgres:16-alpine
    environment:
      POSTGRES_DB: keycloak
      POSTGRES_USER: keycloak
      POSTGRES_PASSWORD: keycloak_password
    volumes:
      - postgres_data:/var/lib/postgresql/data
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U keycloak"]
      interval: 10s
      timeout: 5s
      retries: 5
    networks:
      - backend

  # ===== Keycloak =====
  keycloak:
    image: quay.io/keycloak/keycloak:25.0
    command: start-dev --import-realm
    environment:
      KC_DB: postgres
      KC_DB_URL_HOST: postgres
      KC_DB_URL_DATABASE: keycloak
      KC_DB_USERNAME: keycloak
      KC_DB_PASSWORD: keycloak_password
      KC_HOSTNAME: localhost
      KC_HOSTNAME_PORT: 8080
      KC_HTTP_ENABLED: "true"
      KC_HEALTH_ENABLED: "true"
      KEYCLOAK_ADMIN: admin
      KEYCLOAK_ADMIN_PASSWORD: admin
    volumes:
      - ./keycloak/realm-export.json:/opt/keycloak/data/import/realm-export.json
    ports:
      - "8080:8080"
    depends_on:
      postgres:
        condition: service_healthy
    healthcheck:
      test: ["CMD-SHELL", "exec 3<>/dev/tcp/localhost/8080 && echo -e 'GET /health/ready HTTP/1.1\r\nHost: localhost\r\n\r\n' >&3 && cat <&3 | grep -q '200'"]
      interval: 30s
      timeout: 10s
      retries: 5
    networks:
      - backend

  # ===== API Gateway (Nginx + OAuth2 Proxy) =====
  oauth2-proxy:
    image: quay.io/oauth2-proxy/oauth2-proxy:v7.6.0
    command:
      - --provider=keycloak-oidc
      - --provider-display-name=Keycloak
      - --oidc-issuer-url=http://keycloak:8080/realms/my-realm
      - --client-id=oauth2-proxy-client
      - --client-secret=oauth2-proxy-secret
      - --cookie-secret=bXktMzItYnl0ZS1iYXNlNjQtZW5jb2RlZC1zZWNyZXQ=
      - --cookie-secure=false
      - --email-domain=*
      - --upstream=static://202
      - --http-address=0.0.0.0:4180
      - --set-xauthrequest=true
      - --pass-access-token=true
      - --pass-authorization-header=true
      - --skip-auth-route=^/api/public/
      - --skip-provider-button=true
    depends_on:
      keycloak:
        condition: service_healthy
    networks:
      - backend

  nginx:
    image: nginx:1.27-alpine
    volumes:
      - ./nginx/nginx.conf:/etc/nginx/nginx.conf:ro
      - ./nginx/conf.d:/etc/nginx/conf.d:ro
    ports:
      - "80:80"
    depends_on:
      - oauth2-proxy
      - user-service
      - order-service
    networks:
      - backend

  # ===== Backend Services =====
  user-service:
    build:
      context: ./services/user-service
      dockerfile: Dockerfile
    environment:
      SPRING_SECURITY_OAUTH2_RESOURCESERVER_JWT_ISSUER_URI: http://keycloak:8080/realms/my-realm
      SPRING_SECURITY_OAUTH2_RESOURCESERVER_JWT_JWK_SET_URI: http://keycloak:8080/realms/my-realm/protocol/openid-connect/certs
      SERVER_PORT: 8081
    ports:
      - "8081:8081"
    depends_on:
      keycloak:
        condition: service_healthy
    networks:
      - backend

  order-service:
    build:
      context: ./services/order-service
      dockerfile: Dockerfile
    environment:
      SPRING_SECURITY_OAUTH2_RESOURCESERVER_JWT_ISSUER_URI: http://keycloak:8080/realms/my-realm
      SPRING_SECURITY_OAUTH2_RESOURCESERVER_JWT_JWK_SET_URI: http://keycloak:8080/realms/my-realm/protocol/openid-connect/certs
      SERVER_PORT: 8082
      # Service account cho gọi payment-service
      KEYCLOAK_SERVICE_ACCOUNT_CLIENT_ID: order-service
      KEYCLOAK_SERVICE_ACCOUNT_CLIENT_SECRET: order-service-secret
    ports:
      - "8082:8082"
    depends_on:
      keycloak:
        condition: service_healthy
    networks:
      - backend

volumes:
  postgres_data:

networks:
  backend:
    driver: bridge

HTMLTAG_273__HTMLTAG_274___6.1 Docker Compose 的 Nginx 設定____HTMLTAG_275__HTMLTAG_276

# nginx/conf.d/default.conf
upstream user-service {
    server user-service:8081;
}

upstream order-service {
    server order-service:8082;
}

upstream oauth2-proxy {
    server oauth2-proxy:4180;
}

server {
    listen 80;
    server_name localhost;

    # ===== OAuth2 Proxy endpoints =====
    location /oauth2/ {
        proxy_pass http://oauth2-proxy;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_set_header X-Auth-Request-Redirect $request_uri;
    }

    location = /oauth2/auth {
        proxy_pass http://oauth2-proxy;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header Content-Length "";
        proxy_pass_request_body off;
    }

    # ===== Public APIs (no auth) =====
    location /api/public/ {
        proxy_pass http://user-service;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
    }

    # ===== Protected: User APIs =====
    location /api/users {
        auth_request /oauth2/auth;
        auth_request_set $user $upstream_http_x_auth_request_user;
        auth_request_set $email $upstream_http_x_auth_request_email;
        auth_request_set $token $upstream_http_x_auth_request_access_token;

        proxy_set_header X-User $user;
        proxy_set_header X-Email $email;
        proxy_set_header Authorization "Bearer $token";

        error_page 401 = /oauth2/sign_in;

        proxy_pass http://user-service;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    }

    # ===== Protected: Order APIs =====
    location /api/orders {
        auth_request /oauth2/auth;
        auth_request_set $token $upstream_http_x_auth_request_access_token;

        proxy_set_header Authorization "Bearer $token";

        error_page 401 = /oauth2/sign_in;

        proxy_pass http://order-service;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
    }

    # ===== Health check =====
    location /health {
        return 200 '{"status":"UP"}';
        add_header Content-Type application/json;
    }
}

HTMLTAG_277__HTMLTAG_278___7。監控與速率限制___HTMLTAG_279__HTMLTAG_280

HTMLTAG_281__HTMLTAG_282___7.1 監控服務帳號代幣___HTMLTAG_283__HTMLTAG_284

預編碼_20

HTMLTAG_285__HTMLTAG_286___7.2 服務帳戶最佳實務___HTMLTAG_287__HTMLTAG_288

___HTMLTAG_291__HTMLTAG_292___#___HTMLTAG_293__HTMLTAG_294___練習___HTMLTAG_295__HTMLTAG_296___說明___HTMLTAG_297__HTMLTAG_298___ ___HTMLTAG_301__HTMLTAG_302___1___HTMLTAG_303__HTMLTAG_304___短期令牌___HTMLTAG_305__HTMLTAG_306___為服務帳戶設定存取權杖短生命週期(5 分鐘)____HTMLTAG_307__1TAG_307__HT_3088___ ___HTMLTAG_309__HTMLTAG_310___2___HTMLTAG_311__HTMLTAG_312___最小權限___HTMLTAG_313__HTMLTAG_314___僅分配所需的最低角色__HTMLTAG_315__HTMLTAG_316___ ___HTMLTAG_317__HTMLTAG_318___3___HTMLTAG_319__HTMLTAG_320___輪替機密___HTMLTAG_321__HTMLTAG_322___定期更改client_secret____HTMLTAG_323__HTMLTAG_324___ ___HTMLTAG_325__HTMLTAG_326___4___HTMLTAG_327__HTMLTAG_328___令牌快取____HTMLTAG_329__HTMLTAG_330___在客戶端快取令牌,過期前刷新_____HTMLTAG_331__HTMLTAG_332_____HTMLTAG_331__HTMLTAG_332_____HTMLTAG_331__HTMLTAG_332_____HTMLTAG_331__HTMLTAG_332 ___HTMLTAG_333__HTMLTAG_334___5___HTMLTAG_335__HTMLTAG_336___單獨的客戶端___HTMLTAG_337__HTMLTAG_338___每個服務使用自己的客戶端,沒有共享憑證___ ___HTMLTAG_341__HTMLTAG_342___6___HTMLTAG_343__HTMLTAG_344___網路策略___HTMLTAG_345__HTMLTAG_346___限制服務之間的網路存取___HTMLTAG_347__HTMLTAG_348___ ___HTMLTAG_349__HTMLTAG_350___7___HTMLTAG_351__HTMLTAG_352___審核日誌記錄___HTMLTAG_353__HTMLTAG_354___記錄服務帳戶令牌請求___HTMLTAG_355__HTMLTAG_356___

HTMLTAG_359__HTMLTAG_360___7.3 閘道速率限制___HTMLTAG_361__HTMLTAG_362

# Nginx rate limiting
http {
    # Định nghĩa rate limit zones
    # $binary_remote_addr: limit per IP
    limit_req_zone $binary_remote_addr zone=api_limit:10m rate=10r/s;

    # Limit per authenticated user (từ JWT sub claim)
    limit_req_zone $http_x_user_id zone=user_limit:10m rate=30r/s;

    server {
        # Apply rate limiting cho API endpoints
        location /api/ {
            limit_req zone=api_limit burst=20 nodelay;
            limit_req zone=user_limit burst=50 nodelay;

            limit_req_status 429;

            # Custom error response cho rate limit
            error_page 429 = @rate_limited;
            # ... proxy_pass ...
        }

        location @rate_limited {
            default_type application/json;
            return 429 '{"error": "Too Many Requests", "message": "Rate limit exceeded. Please retry after a moment."}';
        }
    }
}

HTMLTAG_363__HTMLTAG_364___8。摘要___HTMLTAG_365__HTMLTAG_366

___HTMLTAG_369__HTMLTAG_370___網關____HTMLTAG_371__HTMLTAG_372___複雜性___HTMLTAG_373__HTMLTAG_374___效能____HTMLTAG_375__HTMLTAG_376____374___效能____HTMLTAG_375__HT__TAG_376___最適合___7HTTAGML____43____ ___HTMLTAG_381__HTMLTAG_382___Nginx + lua-resty-openidc___HTMLTAG_383__HTMLTAG_384___中____HTMLTAG_385__HTMLTAG_386___非常高___HTMLTAG_387__HTMLTAG_388___高流量,自訂邏輯___MLTAG_38998___ ___HTMLTAG_391__HTMLTAG_392___Nginx + OAuth2 代理___HTMLTAG_393__HTMLTAG_394___低___HTMLTAG_395__HTMLTAG_396___高___HTMLTAG_397__HTMLTAG_398___1設定 ___HTMLTAG_401__HTMLTAG_402___Kong___HTMLTAG_403__HTMLTAG_404___中___HTMLTAG_405__HTMLTAG_406___高___HTMLTAG_407__HTMLTAG_408___企業、406___72GMLTAG_407__HTMLTAG_408___」企業、外掛程式生態系統___MLGML_40910____4____ ___HTMLTAG_411__HTMLTAG_412___Traefik____HTMLTAG_413__HTMLTAG_414___低___HTMLTAG_415__HTMLTAG_416___高___HTMLTAG_417__HTMLTAG_418_____

___HTMLTAG_423__HTMLTAG_424___API 閘道 + Keycloak 部署清單:HTMLTAG_425__HTMLTAG_426

___HTMLTAG_429__HTMLTAG_430___#___HTMLTAG_431__HTMLTAG_432___類別____HTMLTAG_433__HTMLTAG_434___狀態____HTMLTAG_435__HTMLTAG_436___ ___HTMLTAG_439__HTMLTAG_440___1___HTMLTAG_441__HTMLTAG_442___網關處的 JWT 驗證(JWKS 快取)___HTMLTAG_443__HTMLTAG_444______________________ ___HTMLTAG_447__HTMLTAG_448___2___HTMLTAG_449__HTMLTAG_450___將使用者標頭傳播到下游服務____HTMLTAG_451__HTMLTAG_452___________HTMLTAG_453__HTMLTAG_454___ ___HTMLTAG_455__HTMLTAG_456___3___HTMLTAG_457__HTMLTAG_458___用於內部通訊的服務帳戶___HTMLTAG_459__HTMLTAG_460_______________________HTMLTAG_461__HTMLTAG_462___ ___HTMLTAG_463__HTMLTAG_464___4___HTMLTAG_465__HTMLTAG_466___每個 IP 和每個使用者的速率限制____HTMLTAG_467__HTMLTAG_468_________________HTMLTAG_469__HTMLTAG_470___ ___HTMLTAG_471__HTMLTAG_472___5___HTMLTAG_473__HTMLTAG_474___網關處的 CORS 設定___HTMLTAG_475__HTMLTAG_476_______________________HTMLTAG_477__HTMLTAG_478___ ___HTMLTAG_479__HTMLTAG_480___6___HTMLTAG_481__HTMLTAG_482___運行狀況檢查端點(繞過驗證)___HTMLTAG_483__HTMLTAG_484___________HTMLTAG_485__HTMLTAG_486___ ___HTMLTAG_487__HTMLTAG_488___7___HTMLTAG_489__HTMLTAG_490___網關處的 TLS 終止___HTMLTAG_491__HTMLTAG_492________________HTMLTAG_493__HTMLTAG_491__HTMLTAG_492________________HTMLTAG_493__HTMLTAG_494___ ___HTMLTAG_495__HTMLTAG_496___8___HTMLTAG_497__HTMLTAG_498___記錄與監視___HTMLTAG_499__HTMLTAG_500_____________HTMLTAG_501__HTMLTAG_502___ ___HTMLTAG_503__HTMLTAG_504___9___HTMLTAG_505__HTMLTAG_506___客戶端秘密輪調策略___HTMLTAG_507__HTMLTAG_508____________HTMLTAG_509__HTMLTAG_510___ ___HTMLTAG_511__HTMLTAG_512___10____HTMLTAG_513__HTMLTAG_514___令牌過期與刷新處理___HTMLTAG_515__HTMLTAG_516___________HTMLTAG_517__HTMLTAG_518___

下一個系列將深入探討 Keycloak 叢集、生產部署和效能調整等進階主題。