ในยุคที่ AI API กลายเป็นโครงสร้างพื้นฐานสำคัญของแอปพลิเคชันสมัยใหม่ การควบคุมการเข้าถึง (Access Control) และการจัดการสิทธิ์อย่างเป็นระบบจึงมีความจำเป็นมากขึ้น โดยเฉพาะเมื่อต้องให้บริการ AI หลายรุ่นพร้อมกัน ในบทความนี้ผมจะพาทุกท่านไปเรียนรู้การออกแบบ RBAC (Role-Based Access Control) สำหรับ AI Gateway ตั้งแต่พื้นฐานจนถึงการนำไปใช้จริง

ทำไมต้องมี Access Control สำหรับ AI API

จากประสบการณ์การสร้างระบบ AI Gateway หลายตัว ผมพบว่าปัญหาหลักที่พบบ่อยคือการจัดการ API Key ที่ไม่ดี และขาดการควบคุมว่าใครสามารถใช้โมเดลใดได้บ้าง ซึ่งส่งผลให้เกิดทั้งปัญหาด้านความปลอดภัยและการควบคุมค่าใช้จ่าย

HolySheep AI สมัครที่นี่ เป็นบริการที่รวม AI API หลายผู้ให้บริการไว้ในที่เดียว รองรับการจ่ายเงินผ่าน WeChat และ Alipay พร้อมอัตราแลกเปลี่ยนที่คุ้มค่า ¥1=$1 ช่วยประหยัดค่าใช้จ่ายได้มากกว่า 85% เมื่อเทียบกับการใช้งาน API อย่างเป็นทางการโดยตรง และมีความหน่วงต่ำมากเพียง <50ms พร้อมเครดิตฟรีเมื่อลงทะเบียน

ตารางเปรียบเทียบบริการ AI Gateway

เกณฑ์ HolySheep AI API อย่างเป็นทางการ บริการ Relay ทั่วไป
ราคา GPT-4.1 $8/MTok $60/MTok $15-25/MTok
ราคา Claude Sonnet 4.5 $15/MTok $45/MTok $20-35/MTok
ราคา Gemini 2.5 Flash $2.50/MTok $7.50/MTok $4-6/MTok
ราคา DeepSeek V3.2 $0.42/MTok ไม่มีโดยตรง $0.80-1.50/MTok
การชำระเงิน WeChat/Alipay, USD บัตรเครดิตเท่านั้น บัตรเครดิต/USD
ความหน่วง (Latency) <50ms 80-150ms 100-200ms
RBAC Built-in มี ไม่มี (ต้องซื้อ separate) ขึ้นอยู่กับผู้ให้บริการ

RBAC Permission Model คืออะไร

RBAC หรือ Role-Based Access Control เป็นวิธีการจัดการสิทธิ์การเข้าถึงที่กำหนดสิทธิ์ตามบทบาท (Role) ของผู้ใช้ แทนที่จะกำหนดสิทธิ์ให้แต่ละคนโดยตรง ซึ่งทำให้การจัดการง่ายขึ้นและสอดคล้องกับหลักการ Principle of Least Privilege

โครงสร้างข้อมูลสำหรับ RBAC

// โครงสร้างข้อมูลพื้นฐานสำหรับ RBAC Model

// 1. Permission - สิทธิ์การเข้าถึง
interface Permission {
  id: string;
  name: string;           // เช่น "chat:gpt-4", "admin:users"
  resource: string;       // ทรัพยากรที่เข้าถึง เช่น "chat", "admin"
  action: string;         // การกระทำ เช่น "read", "write", "delete"
  model?: string;         // รุ่น AI ที่อนุญาต (ถ้ามี)
}

// 2. Role - บทบาท
interface Role {
  id: string;
  name: string;           // เช่น "developer", "admin", "viewer"
  permissions: string[]; // รายการ Permission IDs
  rateLimit: {
    requestsPerMinute: number;
    tokensPerDay: number;
  };
}

// 3. User - ผู้ใช้
interface User {
  id: string;
  email: string;
  roles: string[];        // รายการ Role IDs
  apiKey: string;
  isActive: boolean;
  createdAt: Date;
}

// 4. API Key Metadata
interface APIKeyMetadata {
  keyId: string;         // รหัสที่สั้นกว่า key เต็ม
  userId: string;
  permissions: string[];
  rateLimit: RateLimitConfig;
  expiresAt?: Date;
}

การสร้างระบบ RBAC สำหรับ AI Gateway

ในการพัฒนาระบบ AI Gateway ที่มีความปลอดภัยและจัดการได้ง่าย ผมแนะนำให้แบ่งการออกแบบออกเป็น 3 ชั้นหลัก ดังนี้

ชั้นที่ 1: Authentication Layer

// middleware/auth.ts - Authentication Middleware
import { Request, Response, NextFunction } from 'express';

interface AuthConfig {
  apiKeyHeader: string;
  keyPrefix: string;
}

// ตั้งค่าสำหรับ HolySheep AI API
const authConfig: AuthConfig = {
  apiKeyHeader: 'x-api-key',
  keyPrefix: 'hs_'
};

interface TokenPayload {
  userId: string;
  keyId: string;
  permissions: string[];
  rateLimit: {
    requestsPerMinute: number;
    tokensPerDay: number;
  };
}

async function authenticateAPIKey(
  req: Request,
  res: Response,
  next: NextFunction
): Promise {
  const apiKey = req.headers[authConfig.apiKeyHeader] as string;
  
  if (!apiKey) {
    res.status(401).json({
      error: 'missing_api_key',
      message: 'API key is required'
    });
    return;
  }
  
  if (!apiKey.startsWith(authConfig.keyPrefix)) {
    res.status(401).json({
      error: 'invalid_api_key_format',
      message: 'Invalid API key format'
    });
    return;
  }
  
  try {
    // ตรวจสอบ API key กับฐานข้อมูล
    const payload = await verifyAndDecodeKey(apiKey);
    
    if (!payload) {
      res.status(401).json({
        error: 'invalid_api_key',
        message: 'API key is invalid or expired'
      });
      return;
    }
    
    // เก็บข้อมูลผู้ใช้ไว้ใน request
    (req as any).auth = payload;
    next();
    
  } catch (error) {
    res.status(500).json({
      error: 'authentication_error',
      message: 'Failed to authenticate'
    });
  }
}

// ฟังก์ชันตรวจสอบ API key
async function verifyAndDecodeKey(apiKey: string): Promise<TokenPayload | null> {
  // ในการใช้งานจริง ให้ตรวจสอบกับฐานข้อมูล
  // ตัวอย่างนี้ใช้ HolySheep AI
  const response = await fetch('https://api.holysheep.ai/v1/auth/verify', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      'Authorization': Bearer ${apiKey}
    }
  });
  
  if (!response.ok) return null;
  
  return response.json();
}

export { authenticateAPIKey, authConfig };

ชั้นที่ 2: Authorization Layer

// middleware/authorize.ts - Authorization Middleware

interface Permission {
  resource: string;
  action: string;
  model?: string;
}

interface AuthPayload {
  userId: string;
  permissions: string[];
  rateLimit: RateLimitConfig;
}

// สร้าง middleware สำหรับตรวจสอบสิทธิ์
function requirePermission(permission: Permission) {
  return async (req: Request, res: Response, next: NextFunction): Promise<void> => {
    const auth = (req as any).auth as AuthPayload;
    
    if (!auth) {
      res.status(401).json({
        error: 'not_authenticated',
        message: 'Authentication required'
      });
      return;
    }
    
    // ตรวจสอบว่ามีสิทธิ์ที่ต้องการหรือไม่
    const hasPermission = checkPermission(auth.permissions, permission);
    
    if (!hasPermission) {
      res.status(403).json({
        error: 'permission_denied',
        message: Permission denied: ${permission.resource}:${permission.action},
        required: permission
      });
      return;
    }
    
    next();
  };
}

// ตรวจสอบสิทธิ์
function checkPermission(userPermissions: string[], required: Permission): boolean {
  const permissionString = required.model
    ? ${required.resource}:${required.action}:${required.model}
    : ${required.resource}:${required.action};
  
  // ตรวจสอบ permission ที่ตรงกัน
  if (userPermissions.includes(permissionString)) {
    return true;
  }
  
  // ตรวจสอบ wildcard permission
  // เช่น "chat:*" หมายถึงทำได้ทุก action
  const wildcardPermission = required.model
    ? ${required.resource}:*:${required.model}
    : ${required.resource}:*;
  
  if (userPermissions.includes(wildcardPermission)) {
    return true;
  }
  
  // ตรวจสอบ admin permission
  if (userPermissions.includes('admin:*')) {
    return true;
  }
  
  return false;
}

// Pre-defined permission middleware
const chatPermissions = {
  gpt4: requirePermission({ resource: 'chat', action: 'create', model: 'gpt-4' }),
  gpt35: requirePermission({ resource: 'chat', action: 'create', model: 'gpt-3.5-turbo' }),
  claude: requirePermission({ resource: 'chat', action: 'create', model: 'claude-3-sonnet' }),
  gemini: requirePermission({ resource: 'chat', action: 'create', model: 'gemini-pro' }),
  deepseek: requirePermission({ resource: 'chat', action: 'create', model: 'deepseek-v3' }),
  
  // สิทธิ์ในการอ่าน
  readModels: requirePermission({ resource: 'models', action: 'read' }),
  
  // สิทธิ์ในการจัดการ API keys
  manageKeys: requirePermission({ resource: 'apikeys', action: 'write' })
};

export { requirePermission, checkPermission, chatPermissions };

ชั้นที่ 3: Rate Limiting Layer

// middleware/rateLimit.ts - Rate Limiting with Token Bucket

import { Request, Response } from 'express';

// In-memory store (ในการใช้งานจริงควรใช้ Redis)
const rateLimitStore = new Map<string, {
  tokens: number;
  lastRefill: number;
}>();

interface RateLimitConfig {
  requestsPerMinute: number;
  tokensPerDay: number;
}

// Token Bucket Algorithm
class TokenBucket {
  private tokens: number;
  private maxTokens: number;
  private refillRate: number; // tokens per second
  private lastRefill: number;
  
  constructor(maxTokens: number, refillRate: number) {
    this.maxTokens = maxTokens;
    this.refillRate = refillRate;
    this.tokens = maxTokens;
    this.lastRefill = Date.now();
  }
  
  consume(tokens: number = 1): boolean {
    this.refill();
    
    if (this.tokens >= tokens) {
      this.tokens -= tokens;
      return true;
    }
    
    return false;
  }
  
  private refill(): void {
    const now = Date.now();
    const secondsPassed = (now - this.lastRefill) / 1000;
    const tokensToAdd = secondsPassed * this.refillRate;
    
    this.tokens = Math.min(this.maxTokens, this.tokens + tokensToAdd);
    this.lastRefill = now;
  }
  
  getRemainingTokens(): number {
    this.refill();
    return Math.floor(this.tokens);
  }
}

// สร้าง rate limiter middleware
function createRateLimiter(config: RateLimitConfig) {
  const buckets = new Map<string, TokenBucket>();
  
  return async (req: Request, res: Response, next: NextFunction): Promise<void> => {
    const auth = (req as any).auth;
    
    if (!auth) {
      next();
      return;
    }
    
    const userId = auth.userId;
    const keyId = auth.keyId;
    const bucketKey = ${userId}:${keyId};
    
    // สร้าง bucket ถ้ายังไม่มี
    if (!buckets.has(bucketKey)) {
      buckets.set(bucketKey, new TokenBucket(
        config.requestsPerMinute,
        config.requestsPerMinute / 60 // refill rate per second
      ));
    }
    
    const bucket = buckets.get(bucketKey)!;
    
    if (!bucket.consume(1)) {
      res.status(429).json({
        error: 'rate_limit_exceeded',
        message: 'Too many requests. Please try again later.',
        retryAfter: Math.ceil((1 / bucket.getRemainingTokens()) * 60)
      });
      return;
    }
    
    // เพิ่ม headers สำหรับ client
    res.setHeader('X-RateLimit-Limit', config.requestsPerMinute);
    res.setHeader('X-RateLimit-Remaining', bucket.getRemainingTokens());
    
    next();
  };
}

// Rate limiter สำหรับ token usage
async function checkTokenLimit(
  userId: string,
  tokenCost: number,
  dailyLimit: number
): Promise<boolean> {
  // ดึงข้อมูลการใช้งานวันนี้
  const today = new Date().toISOString().split('T')[0];
  const usageKey = usage:${userId}:${today};
  
  // ในการใช้งานจริงควรใช้ Redis
  const currentUsage = parseInt(rateLimitStore.get(usageKey) as any || '0');
  
  if (currentUsage + tokenCost > dailyLimit) {
    return false;
  }
  
  return true;
}

export { createRateLimiter, checkTokenLimit, TokenBucket };

การใช้งานร่วมกับ HolySheep AI API

เมื่อเรามีระบบ RBAC แล้ว ต่อไปจะเป็นการนำไปใช้งานจริงกับ HolySheep AI API ซึ่งมีความหน่วงต่ำมากและราคาประหยัดกว่ามาก

// services/aiProxy.ts - AI Proxy Service กับ HolySheep

interface ChatRequest {
  model: string;
  messages: Array<{
    role: 'system' | 'user' | 'assistant';
    content: string;
  }>;
  temperature?: number;
  maxTokens?: number;
}

interface ProxyConfig {
  baseUrl: string;
  apiKey: string;
  timeout: number;
}

class AIProxyService {
  private config: ProxyConfig;
  
  constructor(config: ProxyConfig) {
    this.config = config;
  }
  
  async chat(request: ChatRequest): Promise<any> {
    const { model, messages, temperature = 0.7, maxTokens = 1000 } = request;
    
    // ตรวจสอบ model ที่อนุญาต
    const allowedModels = [
      'gpt-4', 'gpt-4-turbo', 'gpt-3.5-turbo',
      'claude-3-sonnet', 'claude-3-opus', 'claude-3-haiku',
      'gemini-pro', 'gemini-flash',
      'deepseek-v3', 'deepseek-coder'
    ];
    
    if (!allowedModels.includes(model)) {
      throw new Error(Model '${model}' is not supported);
    }
    
    const response = await fetch(${this.config.baseUrl}/chat/completions, {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
        'Authorization': Bearer ${this.config.apiKey},
        'x-model-name': model
      },
      body: JSON.stringify({
        model: model,
        messages: messages,
        temperature: temperature,
        max_tokens: maxTokens
      }),
      signal: AbortSignal.timeout(this.config.timeout)
    });
    
    if (!response.ok) {
      const error = await response.json();
      throw new Error(error.message || 'API request failed');
    }
    
    return response.json();
  }
  
  // ตัวอย่างการใช้งาน
  async example() {
    const service = new AIProxyService({
      baseUrl: 'https://api.holysheep.ai/v1',
      apiKey: process.env.HOLYSHEEP_API_KEY!,
      timeout: 30000
    });
    
    // ส่งคำขอไปยัง DeepSeek ซึ่งมีราคาถูกที่สุด
    const result = await service.chat({
      model: 'deepseek-v3',
      messages: [
        { role: 'system', content: 'คุณเป็นผู้ช่วยที่เป็นมิตร' },
        { role: 'user', content: 'อธิบายเกี่ยวกับ RBAC' }
      ],
      temperature: 0.7,
      maxTokens: 500
    });
    
    console.log('Response:', result.choices[0].message.content);
    console.log('Usage:', result.usage);
    // { prompt_tokens: 50, completion_tokens: 150, total_tokens: 200 }
  }
}

export { AIProxyService, ChatRequest, ProxyConfig };

การสร้าง API Key Management

// services/apiKeyManager.ts - API Key Management System

import crypto from 'crypto';

interface CreateKeyRequest {
  userId: string;
  name: string;
  permissions: string[];
  rateLimit: {
    requestsPerMinute: number;
    tokensPerDay: number;
  };
  expiresAt?: Date;
}

interface APIKey {
  id: string;
  keyHash: string;
  keyPrefix: string;      // สำหรับแสดงให้ผู้ใช้เห็น
  userId: string;
  name: string;
  permissions: string[];
  rateLimit: {
    requestsPerMinute: number;
    tokensPerDay: number;
  };
  createdAt: Date;
  expiresAt?: Date;
  isActive: boolean;
  lastUsedAt?: Date;
}

class APIKeyManager {
  // สร้าง API Key ใหม่
  static generateKey(): { key: string; keyId: string; keyHash: string } {
    const randomBytes = crypto.randomBytes(32);
    const key = hs_${randomBytes.toString('hex')};
    const keyHash = crypto.createHash('sha256').update(key).digest('hex');
    const keyId = key_${crypto.randomBytes(8).toString('hex')};
    
    return { key, keyId, keyHash };
  }
  
  // บันทึก API Key
  static async create(request: CreateKeyRequest): Promise<{ key: string; apiKey: APIKey }> {
    const { key, keyId, keyHash } = this.generateKey();
    
    const apiKey: APIKey = {
      id: keyId,
      keyHash,
      keyPrefix: key.substring(0, 12) + '...',
      userId: request.userId,
      name: request.name,
      permissions: request.permissions,
      rateLimit: request.rateLimit,
      createdAt: new Date(),
      expiresAt: request.expiresAt,
      isActive: true
    };
    
    // บันทึกลงฐานข้อมูล
    await db.apiKeys.create(apiKey);
    
    // ส่ง key เต็มกลับไปให้ผู้ใช้ครั้งเดียวเท่านั้น
    return { key, apiKey };
  }
  
  // ตรวจสอบ API Key
  static async verify(key: string): Promise<APIKey | null> {
    const keyHash = crypto.createHash('sha256').update(key).digest('hex');
    
    const apiKey = await db.apiKeys.findOne({
      keyHash,
      isActive: true
    });
    
    if (!apiKey) return null;
    
    // ตรวจสอบว่าหมดอายุหรือไม่
    if (apiKey.expiresAt && new Date() > apiKey.expiresAt) {
      return null;
    }
    
    // อัปเดต lastUsedAt
    await db.apiKeys.update(apiKey.id, {
      lastUsedAt: new Date()
    });
    
    return apiKey;
  }
  
  // ยกเลิก API Key
  static async revoke(keyId: string): Promise<boolean> {
    const result = await db.apiKeys.update(keyId, {
      isActive: false
    });
    
    return result.modifiedCount > 0;
  }
  
  // ดึงรายการ API Keys ของผู้ใช้
  static async listByUser(userId: string): Promise<APIKey[]> {
    return db.apiKeys.find({
      userId,
      isActive: true
    });
  }
}

// ตัวอย่างการใช้งาน
async function example() {
  // สร้าง API Key สำหรับนักพัฒนา
  const developer = await APIKeyManager.create({
    userId: 'user_123',
    name: 'Developer Key',
    permissions: [
      'chat:create:gpt-3.5-turbo',
      'chat:create:deepseek-v3',
      'models:read'
    ],
    rateLimit: {
      requestsPerMinute: 60,
      tokensPerDay: 100000
    }
  });
  
  console.log('New API Key:', developer.key);
  // แสดงให้ผู้ใช้เห็นครั้งเดียว
  // hs_a1b2c3d4e5f6...
  
  // ตรวจสอบ API Key
  const verified = await APIKeyManager.verify(developer.key);
  if (verified) {
    console.log('Valid key for:', verified.userId);
    console.log('Permissions:', verified.permissions);
  }
}

export { APIKeyManager, APIKey, CreateKeyRequest };

ตัวอย่าง Route ที่สมบูรณ์

// routes/ai.ts - AI Routes พร้อม RBAC

import express from 'express';
import { authenticateAPIKey } from '../middleware/auth';
import { chatPermissions, requirePermission } from '../middleware/authorize';
import { createRateLimiter } from '../middleware/rateLimit';
import { AIProxyService } from '../services/aiProxy';

const router = express.Router();
const aiService = new AIProxyService({
  baseUrl: 'https://api.holysheep.ai/v1',
  apiKey: process.env.HOLYSHEEP_API_KEY!,
  timeout: 30000
});

// Middleware สำหรับทุกเส้นทาง
router.use(authenticateAPIKey);

// Route สำหรับ Chat Completion
router.post('/chat/completions',
  // ตรวจสอบสิทธิ์ตาม model
  async (req: Request, res: Response, next: NextFunction) => {
    const model = req.body.model;
    
    // เลือก middleware ที่เหมาะสม
    const permissionMiddleware = getPermissionMiddleware(model);
    permissionMiddleware(req, res, next);
  },
  // Rate limiting
  createRateLimiter({
    requestsPerMinute: 60,
    tokensPerDay: 100000
  }),
  // Handler
  async (req: Request, res: Response) => {
    try {
      const result = await aiService.chat(req.body);
      res.json(result);
    } catch (error) {
      res.status(500).json({
        error: 'api_error',
        message: error.message
      });
    }
  }
);

// Route สำหรับดูรายการ Models
router.get('/models',
  chatPermissions.readModels,
  async (req: Request, res: Response) => {
    const models = [
      { id: 'gpt-4', name: 'GPT-4', provider: 'OpenAI', price: 8 },
      { id: 'gpt-3.5-turbo', name: 'GPT-3.5 Turbo', provider: 'OpenAI', price: 2 },
      { id: 'claude-3-sonnet', name: 'Claude 3 Sonnet', provider: 'Anthropic', price: 15 },
      { id: 'deepseek-v3', name: 'DeepSeek V3', provider: 'DeepSeek', price: 0.42 },
      { id: 'gemini-pro', name: 'Gemini Pro', provider: 'Google', price: 2.50 }
    ];
    
    res.json({ models });
  }
);

// Route สำหรับดู Usage
router.get('/usage',
  requirePermission({ resource: 'usage', action: 'read' }),
  async (req: Request, res: Response) => {
    const auth = (req as any).auth;
    
    // ดึงข้อมูลการใช้งาน
    const usage = await getUserUsage(auth.userId);
    
    res.json({
      today: {
        tokens: usage.todayTokens,
        requests: usage.todayRequests
      },
      limits: auth.rateLimit
    });
  }
);

// ฟังก์ชันเลือก middleware ตาม model
function getPermissionMiddleware(model: string) {
  switch (model) {
    case 'gpt-4':
    case 'gpt-4-turbo':
      return chatPermissions.gpt4;
    case 'gpt-3.5-turbo':
      return chatPermissions.gpt35;
    case '