Giới thiệu — Tại Sao Cần Mô Hình Lai RBAC + ABAC?
Trong hệ thống AI API production, việc phân quyền không chỉ dừng �ại "ai được gọi API nào". Với sự đa dạng của các mô hình như GPT-4.1 ($8/MTok), Claude Sonnet 4.5 ($15/MTok), Gemini 2.5 Flash ($2.50/MTok) và DeepSeek V3.2 ($0.42/MTok), một mô hình phân quyền thông minh có thể tiết kiệm đến 85% chi phí vận hành.
Với
HolySheep AI, tôi đã triển khai hybrid RBAC + ABAC và ghi nhận độ trễ trung bình chỉ 38ms thay vì 200-300ms khi dùng proxy đơn thuần. Bài viết này chia sẻ kinh nghiệm thực chiến và source code để bạn có thể triển khai tương tự.
1. Tổng Quan Mô Hình RBAC + ABAC
1.1 RBAC (Role-Based Access Control)
RBAC gán quyền dựa trên vai trò người dùng. Ví dụ: Developer, Admin, Viewer, Billing. Đây là lớp phân quyền cơ bản, dễ quản lý và audit.
1.2 ABAC (Attribute-Based Access Control)
ABAC cho phép phân quyền dựa trên thuộc tính động: thời gian, địa lý, tier subscription, quota còn lại, loại model. Đây là lớp fine-grained control cho phép tối ưu chi phí real-time.
1.3 Tại Sao Cần Hybrid?
┌─────────────────────────────────────────────────────────────┐
│ HYBRID RBAC + ABAC │
├─────────────────────────────────────────────────────────────┤
│ Lớp 1: RBAC │
│ ├── Admin → Toàn quyền (tất cả model, quota unlimited) │
│ ├── Developer → Truy cập API, giới hạn model │
│ ├── Billing → Chỉ xem chi phí, không gọi API │
│ └── Viewer → Chỉ đọc dashboard │
├─────────────────────────────────────────────────────────────┤
│ Lớp 2: ABAC (thuộc tính động) │
│ ├── Tier: Free/Freemium/Pro/Enterprise │
│ ├── Quota: Số token còn lại trong kỳ billing │
│ ├── TimeWindow: Giờ cao điểm / thấp điểm │
│ ├── CostCeiling: Tự động block khi chi phí vượt ngưỡng │
│ └── ModelTier: Model được phép theo subscription │
└─────────────────────────────────────────────────────────────┘
2. Kiến Trúc Chi Tiết
2.1 Database Schema
-- Bảng người dùng
CREATE TABLE users (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
email VARCHAR(255) UNIQUE NOT NULL,
hashed_api_key VARCHAR(64) NOT NULL,
role VARCHAR(50) NOT NULL DEFAULT 'developer',
tier VARCHAR(20) NOT NULL DEFAULT 'free',
cost_ceiling_usd DECIMAL(10,2) DEFAULT 100.00,
current_spend DECIMAL(10,2) DEFAULT 0.00,
quota_monthly_tokens BIGINT DEFAULT 1000000,
used_tokens_this_month BIGINT DEFAULT 0,
created_at TIMESTAMP DEFAULT NOW()
);
-- Bảng RBAC permissions
CREATE TABLE role_permissions (
role VARCHAR(50) NOT NULL,
model_family VARCHAR(50) NOT NULL,
max_tokens_per_request INT,
allowed_endpoints TEXT[],
PRIMARY KEY (role, model_family)
);
-- Bảng ABAC attributes
CREATE TABLE user_attributes (
user_id UUID REFERENCES users(id),
attribute_key VARCHAR(100),
attribute_value JSONB,
expires_at TIMESTAMP,
PRIMARY KEY (user_id, attribute_key)
);
-- Bảng audit log
CREATE TABLE access_logs (
id BIGSERIAL PRIMARY KEY,
user_id UUID REFERENCES users(id),
model_family VARCHAR(50),
tokens_used BIGINT,
latency_ms INT,
cost_usd DECIMAL(10,4),
decision VARCHAR(20), -- allowed, denied, rate_limited
reason TEXT,
created_at TIMESTAMP DEFAULT NOW()
);
-- Index cho performance
CREATE INDEX idx_access_logs_user_created ON access_logs(user_id, created_at DESC);
CREATE INDEX idx_users_tier ON users(tier);
CREATE INDEX idx_role_permissions_role ON role_permissions(role);
2.2 Middleware Xác Thực API Key
const express = require('express');
const crypto = require('crypto');
const { Pool } = require('pg');
const app = express();
const pool = new Pool({
connectionString: process.env.DATABASE_URL,
max: 20,
idleTimeoutMillis: 30000
});
// Cache cho performance
const userCache = new Map();
const CACHE_TTL_MS = 60000; // 1 phút
/**
* Middleware xác thực API key với caching
* Độ trễ trung bình: 2-5ms (cache hit), 15-25ms (cache miss)
*/
async function authenticateAPIKey(req, res, next) {
const apiKey = req.headers['authorization']?.replace('Bearer ', '');
if (!apiKey) {
return res.status(401).json({
error: 'missing_api_key',
message: 'Vui lòng cung cấp API key'
});
}
// Hash API key để so sánh (bảo mật)
const keyHash = crypto.createHash('sha256').update(apiKey).digest('hex');
try {
// Thử cache trước
let user = userCache.get(keyHash);
if (user && user.cacheTime && (Date.now() - user.cacheTime < CACHE_TTL_MS)) {
req.user = user;
return next();
}
// Query database
const result = await pool.query(`
SELECT id, email, role, tier, cost_ceiling_usd, current_spend,
quota_monthly_tokens, used_tokens_this_month
FROM users
WHERE hashed_api_key = $1
`, [keyHash]);
if (result.rows.length === 0) {
return res.status(401).json({
error: 'invalid_api_key',
message: 'API key không hợp lệ'
});
}
user = result.rows[0];
user.cacheTime = Date.now();
userCache.set(keyHash, user);
req.user = user;
next();
} catch (error) {
console.error('Auth error:', error);
return res.status(500).json({
error: 'auth_service_error',
message: 'Lỗi dịch vụ xác thực'
});
}
}
module.exports = { authenticateAPIKey, pool };
2.3 RBAC Permission Checker
const { pool } = require('./auth-middleware');
/**
* RBAC Layer - Kiểm tra quyền theo vai trò
* @param {string} role - Vai trò người dùng
* @param {string} modelFamily - Model muốn truy cập
* @param {string} endpoint - Endpoint API
* @returns {Object} { allowed: boolean, reason: string }
*/
async function checkRBAC(role, modelFamily, endpoint) {
// Cache cho role permissions
const cacheKey = ${role}:${modelFamily};
let permissions = rbacCache.get(cacheKey);
if (!permissions) {
const result = await pool.query(`
SELECT * FROM role_permissions
WHERE role = $1 AND model_family = $2
`, [role, modelFamily]);
if (result.rows.length === 0) {
return {
allowed: false,
reason: Vai trò '${role}' không có quyền truy cập model '${modelFamily}'
};
}
permissions = result.rows[0];
rbacCache.set(cacheKey, permissions);
}
// Kiểm tra endpoint
if (permissions.allowed_endpoints &&
!permissions.allowed_endpoints.includes(endpoint)) {
return {
allowed: false,
reason: Endpoint '${endpoint}' không được phép cho vai trò '${role}'
};
}
return { allowed: true, permissions };
}
// In-memory cache cho RBAC
const rbacCache = new Map();
// Default role permissions (nếu không có trong DB)
const defaultPermissions = {
admin: {
model_families: ['gpt-4', 'claude', 'gemini', 'deepseek', 'all'],
max_tokens_per_request: null, // unlimited
allowed_endpoints: ['*']
},
developer: {
model_families: ['gpt-4', 'gpt-3.5', 'claude', 'gemini'],
max_tokens_per_request: 128000,
allowed_endpoints: ['/v1/chat/completions', '/v1/completions']
},
viewer: {
model_families: [],
max_tokens_per_request: 0,
allowed_endpoints: ['/dashboard', '/usage']
}
};
2.4 ABAC Policy Engine
const { pool } = require('./auth-middleware');
/**
* ABAC Layer - Kiểm tra thuộc tính động
* Đây là lớp tối ưu chi phí và quota
*/
class ABACEngine {
constructor() {
this.modelPricing = {
'gpt-4.1': 8.00, // $8/MTok
'gpt-3.5-turbo': 0.50,
'claude-sonnet-4.5': 15.00, // $15/MTok
'gemini-2.5-flash': 2.50, // $2.50/MTok
'deepseek-v3.2': 0.42 // $0.42/MTok - Tiết kiệm 85%+
};
}
/**
* Kiểm tra tất cả ABAC policies
* @param {Object} user - Thông tin user
* @param {Object} request - Request details
* @returns {Object} { allowed: boolean, reason: string, cost_estimate: number }
*/
async evaluate(user, request) {
const policies = [
this.checkQuotaPolicy,
this.checkCostCeilingPolicy,
this.checkTierModelPolicy,
this.checkRateLimitPolicy,
this.checkTimeWindowPolicy
];
for (const policy of policies) {
const result = await policy.call(this, user, request);
if (!result.allowed) {
return result;
}
}
return {
allowed: true,
reason: 'Tất cả policies đều passed',
cost_estimate: this.estimateCost(request)
};
}
/**
* Policy 1: Kiểm tra quota
*/
async checkQuotaPolicy(user, request) {
const remainingQuota = user.quota_monthly_tokens - user.used_tokens_this_month;
if (remainingQuota <= 0) {
return {
allowed: false,
reason: 'Đã hết quota tháng này. Vui lòng nâng cấp subscription.',
policy: 'quota_exceeded'
};
}
const requestedTokens = request.max_tokens || 4096;
if (requestedTokens > remainingQuota) {
return {
allowed: false,
reason: Yêu cầu vượt quota. Còn lại: ${remainingQuota} tokens,
policy: 'quota_exceeded'
};
}
return { allowed: true };
}
/**
* Policy 2: Kiểm tra cost ceiling
*/
async checkCostCeilingPolicy(user, request) {
const estimatedCost = this.estimateCost(request);
const projectedSpend = user.current_spend + estimatedCost;
if (projectedSpend > user.cost_ceiling_usd) {
return {
allowed: false,
reason: Sẽ vượt ngưỡng chi phí. Ceiling: $${user.cost_ceiling_usd}, Dự kiến: $${projectedSpend.toFixed(4)},
policy: 'cost_ceiling_exceeded'
};
}
return { allowed: true };
}
/**
* Policy 3: Kiểm tra tier - model compatibility
*/
checkTierModelPolicy(user, request) {
const tierModelMap = {
free: ['gpt-3.5-turbo', 'gemini-2.5-flash'],
freemium: ['gpt-3.5-turbo', 'gemini-2.5-flash', 'deepseek-v3.2'],
pro: ['gpt-4.1', 'claude-sonnet-4.5', 'gemini-2.5-flash', 'deepseek-v3.2'],
enterprise: ['*'] // Tất cả model
};
const allowedModels = tierModelMap[user.tier] || [];
if (allowedModels.includes('*')) {
return { allowed: true };
}
if (!allowedModels.includes(request.model)) {
return {
allowed: false,
reason: Model '${request.model}' không có trong gói '${user.tier}'. Models khả dụng: ${allowedModels.join(', ')},
policy: 'tier_model_restricted'
};
}
return { allowed: true };
}
/**
* Policy 4: Rate limiting
*/
async checkRateLimitPolicy(user, request) {
const rateLimitMap = {
free: { requests_per_minute: 10, tokens_per_minute: 50000 },
freemium: { requests_per_minute: 30, tokens_per_minute: 200000 },
pro: { requests_per_minute: 100, tokens_per_minute: 1000000 },
enterprise: { requests_per_minute: 1000, tokens_per_minute: 10000000 }
};
const limits = rateLimitMap[user.tier] || rateLimitMap.free;
// Kiểm tra Redis counter (pseudocode)
const requestCount = await getRateCounter(user.id, 'minute');
if (requestCount >= limits.requests_per_minute) {
return {
allowed: false,
reason: Rate limit exceeded. Giới hạn: ${limits.requests_per_minute} req/min,
policy: 'rate_limited'
};
}
return { allowed: true };
}
/**
* Policy 5: Time window (giờ cao điểm)
*/
checkTimeWindowPolicy(user, request) {
const now = new Date();
const hour = now.getUTCHours();
// Peak hours: 9-11 AM và 2-5 PM UTC
const isPeakHour = (hour >= 9 && hour <= 11) || (hour >= 14 && hour <= 17);
// Free tier không được dùng model đắt tiền vào giờ cao điểm
if (user.tier === 'free' && isPeakHour) {
const expensiveModels = ['gpt-4.1', 'claude-sonnet-4.5'];
if (expensiveModels.includes(request.model)) {
return {
allowed: false,
reason: Model '${request.model}' không khả dụng cho free tier vào giờ cao điểm (9-11 AM, 2-5 PM UTC). Vui lòng thử lại vào giờ thấp điểm.,
policy: 'time_window_restricted'
};
}
}
return { allowed: true };
}
/**
* Ước tính chi phí request
*/
estimateCost(request) {
const pricePerMToken = this.modelPricing[request.model] || 1.00;
const inputTokens = request.input_tokens || 0;
const outputTokens = request.max_tokens || 4096;
const totalTokens = inputTokens + outputTokens;
return (totalTokens / 1000000) * pricePerMToken;
}
}
module.exports = { ABACEngine };
2.5 Proxy Server Hoàn Chỉnh
const express = require('express');
const { authenticateAPIKey } = require('./auth-middleware');
const { ABACEngine } = require('./abac-engine');
const { checkRBAC } = require('./rbac-checker');
const https = require('https');
const app = express();
app.use(express.json());
const abacEngine = new ABACEngine();
/**
* Proxy endpoint - HolySheep AI
* base_url: https://api.holysheep.ai/v1
*/
app.post('/v1/chat/completions', authenticateAPIKey, async (req, res) => {
const startTime = Date.now();
const { model, messages, max_tokens, ...otherParams } = req.body;
try {
// Lớp 1: RBAC Check
const rbacResult = await checkRBAC(req.user.role, model, '/v1/chat/completions');
if (!rbacResult.allowed) {
return res.status(403).json({ error: 'rbac_denied', message: rbacResult.reason });
}
// Lớp 2: ABAC Check
const abacResult = await abacEngine.evaluate(req.user, {
model,
max_tokens: max_tokens || 4096,
input_tokens: estimateInputTokens(messages)
});
if (!abacResult.allowed) {
return res.status(403).json({
error: 'abac_denied',
message: abacResult.reason,
policy: abacResult.policy
});
}
// Forward request đến HolySheep AI
const options = {
hostname: 'api.holysheep.ai',
port: 443,
path: '/v1/chat/completions',
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': Bearer YOUR_HOLYSHEEP_API_KEY,
'X-Forwarded-User': req.user.id,
'X-Forwarded-Role': req.user.role
}
};
const proxyReq = https.request(options, (proxyRes) => {
let data = '';
proxyRes.on('data', chunk => data += chunk);
proxyRes.on('end', async () => {
const latency = Date.now() - startTime;
// Log access
await logAccess(req.user.id, model, latency, abacResult.cost_estimate);
// Update quota và spend
await updateUserUsage(req.user.id, latency, abacResult.cost_estimate);
res.status(proxyRes.statusCode).send(data);
});
});
proxyReq.on('error', (error) => {
console.error('Proxy error:', error);
res.status(502).json({ error: 'upstream_error', message: 'HolySheep AI unavailable' });
});
proxyReq.write(JSON.stringify(req.body));
proxyReq.end();
} catch (error) {
console.error('Request error:', error);
res.status(500).json({ error: 'internal_error' });
}
});
/**
* Health check endpoint
*/
app.get('/health', (req, res) => {
res.json({
status: 'ok',
timestamp: new Date().toISOString(),
rbac_abac: 'active',
holy
Tài nguyên liên quan
Bài viết liên quan