0

Series Thực hành SSO | Bài 5: Tích hợp SSO vào Microservices với API Gateway Pattern

Chào anh em! Sau 4 bài thực hành, chúng ta đã có một SSO Server cấp Token và một Client App biết cách lấy Token (bằng Authorization Code) và làm mới Token (Refresh Token).

Nhưng câu hỏi đặt ra là: Khi Client App cầm cái Access Token đó để gọi API lấy dữ liệu, nó sẽ gọi đi đâu? Nếu hệ thống của anh em là Monolithic (kiến trúc nguyên khối), Client cứ ném thẳng Token lên server là xong. Nhưng nếu hệ thống là Microservices (gồm Service A, Service B, Service C độc lập), việc mỗi Service phải tự mang Token đi hỏi SSO Server xem "Token này có hợp lệ không?" sẽ tạo ra một nút thắt cổ chai (bottleneck) khổng lồ về network.

Hôm nay, chúng ta sẽ giải quyết bài toán này bằng API Gateway Pattern.

1. Vai trò của API Gateway trong hệ thống SSO

Trong kiến trúc Microservices, API Gateway đóng vai trò như một trạm thu phí đường bộ. Tất cả các request từ Mobile App, Web App hay Client App đều KHÔNG ĐƯỢC phép gọi thẳng vào các Microservices bên dưới. Mọi thứ phải đi qua cổng API Gateway.

Quy trình xác thực diễn ra như sau:

  1. Client gửi Request kèm Access Token (chuẩn Bearer) lên API Gateway.
  2. API Gateway sẽ làm nhiệm vụ kiểm tra Token này (xác thực chữ ký, kiểm tra hạn sử dụng).
  3. Nếu Token xịn, API Gateway sẽ bóc tách thông tin (ví dụ: user_id = 15, role = admin).
  4. API Gateway đính kèm cái user_id đó vào HTTP Header (ví dụ: X-User-Id: 15) và chuyển tiếp (Proxy/Forward) request xuống cho Microservice tương ứng (vd: Ticket Service).
  5. Các Microservices bên dưới hoàn toàn tin tưởng API Gateway. Chúng không cần quan tâm Token là gì, chỉ cần đọc Header X-User-Id để xử lý nghiệp vụ.

2. Thực hành: Xây dựng API Gateway bằng Laravel

Trong thực tế, người ta hay dùng Kong, KrakenD hoặc Nginx làm Gateway cho nhẹ. Nhưng để anh em dễ hiểu luồng logic, chúng ta sẽ dùng chính Laravel để mô phỏng một API Gateway cơ bản.

Khởi tạo project thứ 3:

composer create-project laravel/laravel api-gateway

Bước 1: Viết Middleware Xác thực Token tại Gateway

API Gateway cần phải biết Token do SSO cấp có hợp lệ hay không. Vì Passport mặc định dùng chuẩn JWT (JSON Web Token), Gateway có thể tự giải mã và xác thực chữ ký (Signature) bằng Public Key của SSO Server mà không cần phải gọi API hỏi lại SSO.

Tuy nhiên, cách đơn giản và phổ biến nhất (Introspection) là Gateway sẽ gọi một API nội bộ sang SSO để check.

Mở api-gateway, tạo Middleware:

php artisan make:middleware VerifySSOToken

Code logic kiểm tra trong Middleware:

<?php

namespace App\Http\Middleware;

use Closure;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Http;

class VerifySSOToken
{
    public function handle(Request $request, Closure $next)
    {
        $token = $request->bearerToken();

        if (!$token) {
            return response()->json(['error' => 'Thiếu Access Token'], 401);
        }

        // Gọi sang SSO Server để xác thực Token (Introspection endpoint)
        // Lưu ý: Ở Production, đoạn này nên có Redis Cache trong vài phút để giảm tải cho SSO
        $response = Http::withToken($token)
                        ->get(env('SSO_SERVER_URL') . '/api/validate-token');

        if ($response->failed()) {
            return response()->json(['error' => 'Token không hợp lệ hoặc đã hết hạn'], 401);
        }

        $userData = $response->json();

        // Gắn thông tin User vào Request hiện tại để chuyển đi tiếp
        $request->attributes->add(['sso_user_id' => $userData['id']]);
        $request->attributes->add(['sso_user_role' => $userData['role']]);

        return $next($request);
    }
}

Bước 2: Chuyển tiếp (Forward) Request xuống Microservice

Sau khi Middleware xác nhận Token ok, API Gateway sẽ định tuyến request xuống Service đích (Ví dụ: Ticket Service - quản lý vé tàu).

Trong routes/api.php của API Gateway:

use Illuminate\Support\Facades\Http;
use App\Http\Middleware\VerifySSOToken;

Route::middleware([VerifySSOToken::class])->group(function () {
    
    // Route hứng request lấy danh sách vé
    Route::get('/tickets', function (Request $request) {
        
        $userId = $request->attributes->get('sso_user_id');
        
        // Chuyển tiếp Request xuống Ticket Microservice (chạy nội bộ)
        // Gắn thêm Header X-User-Id để Service bên dưới biết ai đang gọi
        $ticketServiceResponse = Http::withHeaders([
            'X-User-Id' => $userId,
            'Accept'    => 'application/json'
        ])->get('http://internal-ticket-service:8000/api/my-tickets');
        
        // Trả kết quả về cho Client
        return response($ticketServiceResponse->body(), $ticketServiceResponse->status())
                ->withHeaders($ticketServiceResponse->headers());
    });

});

3. Tại Microservice: Nhận dữ liệu không cần xác thực

Lúc này, tại con server Ticket Service (một hệ thống độc lập hoàn toàn), anh em không cần cài cắm Passport, không cần cấu hình JWT lằng nhằng nữa.

Đoạn code trong Controller của Ticket Service chỉ đơn giản là:

public function getMyTickets(Request $request)
{
    // Đọc thẳng User ID từ Header do API Gateway truyền xuống
    $userId = $request->header('X-User-Id');

    if (!$userId) {
        return response()->json(['error' => 'Unauthorized Access'], 403);
    }

    // Query database lấy vé của user này
    $tickets = Ticket::where('user_id', $userId)->get();

    return response()->json($tickets);
}

Bảo mật: Nhớ cấu hình Firewall/Security Group ở phía hạ tầng (Docker/AWS) để Ticket Service CHỈ nhận các HTTP Request đến từ dải IP của API Gateway. Bất kỳ ai gọi thẳng vào Ticket Service đều bị chặn ở tầng mạng.

Tổng kết toàn Series

Anh em vừa cùng nhau đi qua một hành trình dài và cực kỳ chuyên sâu về Single Sign-On.

1 .Chúng ta biến Laravel thành IdP (SSO Server) bằng Passport. 2. Hiểu luồng Authorization Code bảo mật để cấp Token. 3. Xử lý mượt mà bài toán Refresh Token và Single Logout (SLO). 4. Cuối cùng là thiết kế kiến trúc chuẩn chỉnh với API Gateway để mở đường cho Microservices.

Kiến thức này không chỉ giúp anh em tự tin code tính năng Đăng nhập bằng Google/Facebook, mà còn đủ sức thiết kế hệ thống định danh nội bộ cho các tập đoàn lớn có hàng tá phần mềm vệ tinh.

Code hệ thống chưa bao giờ là dễ, nhưng khi đã "thông" được tư duy thiết kế, anh em sẽ thấy nó là một bộ môn nghệ thuật cực kỳ cuốn hút. Chúc anh em viết code ít bug và server luôn khỏe mạnh nhé!


All rights reserved

Viblo
Hãy đăng ký một tài khoản Viblo để nhận được nhiều bài viết thú vị hơn.
Đăng kí