การใช้ Middleware สำหรับ Authentication
ในบทที่แล้ว เราได้เรียนรู้เกี่ยวกับการสร้าง Middleware ใน PHP และวิธีการใช้งานเบื้องต้น ในบทนี้ เราจะนำความรู้เหล่านั้นมาประยุกต์ใช้กับการทำ Authentication หรือการตรวจสอบสิทธิ์การเข้าถึง API ของเรา โดยจะเน้นไปที่การใช้ Middleware เพื่อตรวจสอบ API Key ก่อนที่จะอนุญาตให้เข้าถึง Endpoint ที่ต้องการ
ทำไมต้องใช้ Middleware สำหรับ Authentication?
การใช้ Middleware สำหรับ Authentication มีข้อดีหลายประการ:
- Reusable Code: เราสามารถเขียน Logic การตรวจสอบสิทธิ์เพียงครั้งเดียวและนำไปใช้กับหลาย Endpoint ได้
- Centralized Logic: การจัดการ Authentication Logic อยู่ในที่เดียว ทำให้ง่ายต่อการแก้ไขและบำรุงรักษา
- Clean Code: ช่วยให้ Controller หรือ Handler ของ Endpoint มีความสะอาดและมุ่งเน้นไปที่ Business Logic หลัก
- Flexible: สามารถปรับเปลี่ยน Logic การตรวจสอบสิทธิ์ได้ง่าย โดยไม่ต้องแก้ไข Code ใน Controller
การตรวจสอบ API Key ใน Middleware
ขั้นตอนแรกคือการสร้าง Middleware ที่จะทำหน้าที่ตรวจสอบ API Key ที่ส่งมาพร้อมกับ Request ตัวอย่างเช่น เราจะตรวจสอบว่ามี Header ชื่อ X-API-Key หรือไม่ และมีค่าตรงกับ API Key ที่เรากำหนดไว้หรือไม่
<?php
class ApiKeyMiddleware {
private $apiKey;
public function __construct(string $apiKey) {
$this->apiKey = $apiKey;
}
public function handle($request, $next) {
$apiKeyHeader = $request->getHeader('X-API-Key');
if (empty($apiKeyHeader)) {
// ไม่พบ API Key ใน Header
return $this->errorResponse('API Key is required', 401);
}
$apiKey = $apiKeyHeader[0]; // ดึงค่า API Key จาก Array
if ($apiKey !== $this->apiKey) {
// API Key ไม่ถูกต้อง
return $this->errorResponse('Invalid API Key', 403);
}
// API Key ถูกต้อง เรียก Handler ถัดไป
return $next($request);
}
private function errorResponse(string $message, int $statusCode) {
header('Content-Type: application/json');
http_response_code($statusCode);
echo json_encode(['error' => $message]);
return; // สำคัญ: ต้อง return เพื่อหยุดการทำงาน
}
}
คำอธิบาย Code:
- Class
ApiKeyMiddlewareรับค่า API Key ผ่าน Constructor - Method
handleรับ Request และ Closure$nextเป็น Parameter - ตรวจสอบว่ามี Header ชื่อ
X-API-Keyหรือไม่ ถ้าไม่มีให้ส่ง Error Response กลับไป - ตรวจสอบว่าค่าของ
X-API-Keyตรงกับ API Key ที่กำหนดไว้หรือไม่ ถ้าไม่ตรงให้ส่ง Error Response กลับไป - ถ้า API Key ถูกต้อง ให้เรียก Closure
$nextเพื่อส่ง Request ไปยัง Handler ถัดไป - Method
errorResponseสร้าง JSON Response ที่มี Error Message และ HTTP Status Code
ข้อควรระวัง:
- อย่า Hardcode API Key ใน Code ควรเก็บไว้ใน Environment Variable หรือ Configuration File
- ควรใช้ HTTPS เพื่อป้องกันการดักจับ API Key
การให้สิทธิ์การเข้าถึง (Authorization)
นอกจากการตรวจสอบว่า API Key ถูกต้องแล้ว เราอาจต้องการให้สิทธิ์การเข้าถึงที่แตกต่างกัน ขึ้นอยู่กับ API Key ที่ใช้ ตัวอย่างเช่น API Key สำหรับ Admin อาจสามารถเข้าถึง Endpoint ได้ทุก Endpoint ในขณะที่ API Key สำหรับ User ทั่วไปอาจเข้าถึงได้เฉพาะบาง Endpoint เท่านั้น
เราสามารถ Implement Logic นี้ได้ใน Middleware โดยการตรวจสอบ API Key และกำหนด Role หรือ Permission ให้กับ Request จากนั้นจึงตรวจสอบว่า Request นั้นมีสิทธิ์ในการเข้าถึง Endpoint ที่ต้องการหรือไม่
<?php
class ApiKeyMiddleware {
private $apiKeys = [
'admin' => 'ADMIN_API_KEY',
'user' => 'USER_API_KEY',
];
private $roles = [
'ADMIN_API_KEY' => ['role' => 'admin', 'permissions' => ['*']], // admin มีสิทธิ์ทุกอย่าง
'USER_API_KEY' => ['role' => 'user', 'permissions' => ['read', 'write']], // user มีสิทธิ์อ่าน/เขียน
];
public function handle($request, $next, $requiredPermission = null) {
$apiKeyHeader = $request->getHeader('X-API-Key');
if (empty($apiKeyHeader)) {
return $this->errorResponse('API Key is required', 401);
}
$apiKey = $apiKeyHeader[0];
if (!in_array($apiKey, array_values($this->apiKeys))) {
return $this->errorResponse('Invalid API Key', 403);
}
// ดึง role และ permission จาก API Key
$roleData = $this->roles[$apiKey];
$request->attributes->set('role', $roleData['role']); // เก็บ role ไว้ใน request
$request->attributes->set('permissions', $roleData['permissions']); // เก็บ permission ไว้ใน request
if ($requiredPermission !== null && !in_array($requiredPermission, $roleData['permissions'])) {
return $this->errorResponse('Unauthorized', 403);
}
return $next($request);
}
private function errorResponse(string $message, int $statusCode) {
header('Content-Type: application/json');
http_response_code($statusCode);
echo json_encode(['error' => $message]);
return;
}
}
คำอธิบาย Code:
$apiKeysเก็บ API Key แต่ละประเภท (admin, user)$rolesเก็บ Role และ Permission ของแต่ละ API Key- Method
handleรับ Parameter เพิ่มเติมคือ$requiredPermissionซึ่งเป็น Permission ที่ Endpoint นั้นต้องการ - ตรวจสอบว่า API Key มีอยู่ใน
$rolesหรือไม่ - ถ้ามี ให้ดึง Role และ Permission ของ API Key นั้น
- ตรวจสอบว่า Request นั้นมี Permission ที่ Endpoint ต้องการหรือไม่ ถ้าไม่มีให้ส่ง Error Response กลับไป
- เก็บ Role และ Permission ไว้ใน Request Attribute เพื่อให้ Controller สามารถนำไปใช้ได้
ตัวอย่างการใช้งาน:
<?php
// ตัวอย่างการใช้งานใน Controller
$apiKeyMiddleware = new ApiKeyMiddleware();
// Endpoint ที่ต้องการ Permission 'read'
$response = $apiKeyMiddleware->handle($request, function ($request) {
// Logic ของ Endpoint
$role = $request->attributes->get('role');
$permissions = $request->attributes->get('permissions');
// ตรวจสอบ Role และ Permission เพิ่มเติม (ถ้าต้องการ)
if ($role === 'admin' || in_array('read', $permissions)) {
return json_encode(['message' => 'Data retrieved successfully']);
} else {
return json_encode(['error' => 'Unauthorized'], 403);
}
}, 'read');
echo $response;
การส่ง Error Response
เมื่อเกิด Error ใน Middleware เช่น API Key ไม่ถูกต้อง หรือไม่มีสิทธิ์การเข้าถึง เราจะต้องส่ง Error Response กลับไปยัง Client เพื่อแจ้งให้ทราบถึงปัญหาที่เกิดขึ้น
Error Response ควรมีข้อมูลที่จำเป็น เช่น HTTP Status Code และ Error Message
HTTP Status Codes ที่ควรใช้:
- 401 Unauthorized: ใช้เมื่อ API Key ไม่ถูกต้อง หรือไม่มี API Key ส่งมา
- 403 Forbidden: ใช้เมื่อ API Key ถูกต้อง แต่ไม่มีสิทธิ์ในการเข้าถึง Endpoint นั้น
ตัวอย่าง Error Response (JSON):
{
"error": "Invalid API Key"
}
ข้อผิดพลาดที่พบบ่อย:
- ลืมตั้งค่า
Content-Typeเป็นapplication/jsonทำให้ Client ไม่สามารถ Parse Response ได้ - ลืมตั้งค่า HTTP Status Code ให้ถูกต้อง
- ไม่ Return จาก Middleware หลังจากส่ง Error Response ทำให้ Code ยังทำงานต่อไป
- Hardcode API Key ใน Code
- ไม่ใช้ HTTPS
สรุป
ในบทนี้ เราได้เรียนรู้วิธีการใช้ Middleware สำหรับ Authentication ใน API โดยเน้นไปที่การตรวจสอบ API Key และการให้สิทธิ์การเข้าถึง นอกจากนี้ เรายังได้เรียนรู้เกี่ยวกับการส่ง Error Response ที่ถูกต้อง และข้อผิดพลาดที่พบบ่อยในการ Implement Authentication ด้วย Middleware
ในบทถัดไป เราจะเรียนรู้เกี่ยวกับ CORS (Cross-Origin Resource Sharing) และวิธีการตั้งค่า CORS Headers ใน PHP เพื่อให้ API ของเราสามารถเข้าถึงได้จาก Domain อื่น