0

PHP và MySQL Bài 31: Kiểm thử API với Postman và Bảo mật API bằng JWT (JSON Web Token)

Trong thế giới API, bạn sẽ phải làm quen với một sự thật phũ phàng: Giao diện HTML không còn, và Session/Cookie cũng "chết" theo [cite: x].


1. Postman - "Trình duyệt" dành riêng cho Backend Developer

Vì API chỉ trả về dữ liệu thô (JSON) và nhận dữ liệu ngầm (POST, PUT, DELETE), bạn không thể dùng Google Chrome để kiểm tra các luồng thêm, sửa, xóa được [cite: x]. Lúc này, bạn cần Postman [cite: x].

Postman là một phần mềm miễn phí giúp bạn giả lập bất kỳ loại Request nào gửi lên Server [cite: x].

Cách dùng Postman để test API Thêm thiết bị (POST) ở Bài 30:

  1. Mở Postman, chọn phương thức POST [cite: x].
  2. Nhập URL của API (Ví dụ: http://localhost/api/machines.php) [cite: x].
  3. Chuyển sang tab Body, chọn raw, và đổi kiểu text thành JSON [cite: x].
  4. Nhập cục dữ liệu JSON giả lập [cite: x]:
    {
        "machine_code": "TVM_TEST_99",
        "station_id": 5
    }
    
  5. Bấm Send. Phía dưới màn hình sẽ hiển thị ngay kết quả JSON mà code PHP của bạn trả về cùng với mã trạng thái (201 Created hoặc 400 Bad Request) [cite: x].

2. Tại sao Session "chết" trong thế giới API?

Nhớ lại Bài 16, Session hoạt động dựa trên việc Server gửi cho trình duyệt một cái Cookie chứa PHPSESSID [cite: x].

Tuy nhiên, kiến trúc RESTful API bắt buộc phải tuân theo nguyên tắc Stateless (Không trạng thái) [cite: x]:

  • Mobile App (iOS/Android) không có cơ chế quản lý Cookie tự động như trình duyệt Web [cite: x].
  • Nếu Backend và Frontend (React/Vue) nằm ở hai tên miền khác nhau (Ví dụ: api.hethong.comhethong.com), trình duyệt sẽ chặn Cookie vì lý do bảo mật (CORS) [cite: x].
  • Server API không được phép lưu trữ trạng thái của Client trên RAM (Session) để đảm bảo khả năng mở rộng (Scale) lên hàng ngàn máy chủ [cite: x].

Giải pháp thay thế hoàn hảo nhất hiện nay là JWT (JSON Web Token) [cite: x].


3. JWT (JSON Web Token) là gì?

Thay vì lưu "két sắt" trên Server và giao cho khách "chìa khóa" (Session), JWT đóng gói toàn bộ thông tin của khách (ID, Quyền hạn), ký tên đóng dấu điện tử lên đó, và giao hẳn cho khách cầm [cite: x].

Một chuỗi JWT nhìn bề ngoài giống như một đoạn mã loằng ngoằng, chia làm 3 phần ngăn cách bởi dấu chấm (.) (xxxxxxxx.yyyyyyyy.zzzzzzzz) [cite: x]:

  1. Header (Đỏ): Chứa thông tin về thuật toán mã hóa (Thường là HS256) [cite: x].
  2. Payload (Tím): Chứa thông tin thực sự của người dùng (Ví dụ: user_id = 8899, role = admin). Dữ liệu này chỉ được mã hóa Base64 (ai cũng có thể dịch ra đọc được) [cite: x].
  3. Signature (Xanh): Chữ ký bảo mật. Đây là phần quan trọng nhất. Server sẽ dùng một Secret Key (Khóa bí mật chỉ Server biết) để băm Header và Payload. Nếu ai đó cố tình sửa chữ role=user thành role=admin trong Payload, Chữ ký sẽ bị sai lệch hoàn toàn và Server sẽ từ chối Token đó ngay lập tức [cite: x].

4. Luồng hoạt động của Hệ thống API dùng JWT

Bước 1: Đăng nhập (Tạo Token)

  • Client gửi emailpassword lên API Đăng nhập [cite: x].
  • Backend PHP kiểm tra trong Database. Nếu đúng, PHP sẽ tạo ra một chuỗi JWT (có thời hạn, ví dụ 1 tiếng) và trả về cho Client [cite: x].
  • Client (Mobile/React) sẽ cất chuỗi JWT này vào bộ nhớ cục bộ của thiết bị [cite: x].

Bước 2: Gọi API mang theo Token

  • Khi Client muốn gọi một API cần bảo mật (Ví dụ: Thêm máy bán vé), Client phải đính kèm chuỗi JWT vào HTTP Header với cú pháp [cite: x]: Authorization: Bearer <Chuỗi_JWT>
  • Backend PHP đọc Header, kiểm tra chữ ký Signature của Token. Nếu chữ ký đúng và Token chưa hết hạn, PHP lấy user_id từ trong Token ra và cho phép thực thi lệnh [cite: x].

5. Ví dụ thực tế: Cách nhận Token bằng PHP

Trong thực tế, để tạo và xác thực JWT, chúng ta không tự viết thuật toán từ đầu mà sẽ dùng một thư viện chuẩn (như thư viện firebase/php-jwt) [cite: x].

Dưới đây là cách Backend PHP "đón" và kiểm tra Token khi Client gửi lên [cite: x]:

<?php
// Trích xuất Header Authorization từ Request của Client
$headers = apache_request_headers();

if (isset($headers['Authorization'])) {
    // Chuỗi gửi lên có dạng "Bearer xxxx.yyyy.zzzz" -> Cắt lấy phần token
    $token = str_replace('Bearer ', '', $headers['Authorization']);
    
    try {
        // Sử dụng thư viện JWT để giải mã và xác thực chữ ký bằng Secret Key
        // Hàm decode() sẽ tự động ném ra lỗi nếu Token bị sửa đổi hoặc hết hạn
        $decoded = JWT::decode($token, new Key('CHUOI_BI_MAT_CUA_SERVER', 'HS256'));
        
        // Lấy thông tin user từ Payload
        $userId = $decoded->user_id;
        $role = $decoded->role;
        
        if ($role !== 'admin') {
            http_response_code(403); // Forbidden
            echo json_encode(["message" => "Bạn không có quyền thực hiện hành động này."]);
            exit();
        }
        
        // Cho phép đi tiếp vào logic xử lý API...
        
    } catch (Exception $e) {
        // Token sai, bị sửa đổi, hoặc đã hết hạn
        http_response_code(401); // Unauthorized
        echo json_encode(["message" => "Token không hợp lệ hoặc đã hết hạn."]);
        exit();
    }
} else {
    http_response_code(401);
    echo json_encode(["message" => "Vui lòng cung cấp Token bảo mật."]);
    exit();
}
?>

Tổng kết

Với JWT, hệ thống Backend của bạn đã hoàn toàn độc lập, phi trạng thái (stateless) và sẵn sàng phục vụ cho bất kỳ nền tảng Frontend hay Mobile App nào [cite: x]. Đây chính là tiêu chuẩn vàng của kiến trúc phần mềm hiện đại [cite: x].

Tuy nhiên, trong ví dụ trên, tôi đã nhắc đến việc sử dụng "Thư viện bên thứ ba" (firebase/php-jwt). Trong thế giới PHP hiện đại, chúng ta tải và quản lý các thư viện này như thế nào?


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í