Now.js Framework Documentation
AuthGuard - ระบบป้องกันเส้นทางและควบคุมการเข้าถึง
AuthGuard - ระบบป้องกันเส้นทางและควบคุมการเข้าถึง
เอกสารสำหรับ AuthGuard ซึ่งเป็นระบบป้องกันเส้นทาง และควบคุมการเข้าถึง ของ Now.js Framework
📋 สารบัญ
- ภาพรวม
- การติดตั้งและนำเข้า
- การเริ่มต้นใช้งาน
- การป้องกันเส้นทาง
- การควบคุมการเข้าถึงตามบทบาท
- การตรวจสอบสิทธิ์
- การตรวจสอบแบบกำหนดเอง
- ผลลัพธ์ของ Guard
- การจัดการข้อผิดพลาด
- ตัวอย่างการใช้งาน
- เอกสารอ้างอิง API
- แนวทางปฏิบัติที่ดี
- ข้อผิดพลาดที่พบบ่อย
- ประสิทธิภาพและการปรับแต่ง
ภาพรวม
AuthGuard เป็นระบบตรวจสอบสิทธิ์สำหรับ Router ที่ป้องกันเส้นทางและจัดการ access control ตาม roles และ permissions
ฟีเจอร์หลัก
- ✅ การป้องกันเส้นทาง: ป้องกันเส้นทางตามสถานะการล็อกอิน
- ✅ การควบคุมด้วยบทบาท (Role-Based Access): ตรวจสอบบทบาทของผู้ใช้
- ✅ การตรวจสอบสิทธิ์: ตรวจสอบสิทธิ์การใช้งานเชิงลึก
- ✅ การตรวจสอบแบบกำหนดเอง: รองรับตรรกะตรวจสอบเพิ่มเติม
- ✅ เส้นทางสำหรับบุคคลทั่วไป: กำหนดเส้นทางเฉพาะผู้ใช้ที่ยังไม่ล็อกอิน
- ✅ เส้นทางสาธารณะ: เส้นทางที่เข้าถึงได้โดยไม่ต้องล็อกอิน
- ✅ จำเส้นทางที่ตั้งใจไป: เก็บเส้นทางที่ต้องการก่อนยืนยันตัวตน
- ✅ แคชสถานะการล็อกอิน: ลดการตรวจสอบซ้ำที่ไม่จำเป็น
- ✅ ทำงานร่วมกับ AuthLoadingManager: แสดงสถานะโหลดระหว่างการตรวจสอบ
- ✅ ทำงานร่วมกับ AuthErrorHandler: จัดการข้อผิดพลาดด้านสิทธิ์การเข้าถึง
เมื่อไหร่ควรใช้ AuthGuard
✅ ใช้ AuthGuard เมื่อ:
- ต้องการป้องกันเส้นทางตาม authentication
- ต้องการ role-based access control
- ต้องการตรวจสอบ permissions
- ต้องการ custom authorization logic
- ใช้ Router ร่วมกับ authentication system
❌ ไม่ควรใช้เมื่อ:
- ไม่ได้ใช้ Router
- ไม่ต้องการ route protection
- ทำ manual authorization checks
การติดตั้งและนำเข้า
AuthGuard โหลดมาพร้อมกับ Now.js Framework และพร้อมใช้งานทันทีผ่าน window object:
// ไม่ต้อง import - พร้อมใช้งานทันที
console.log(window.AuthGuard); // อ็อบเจ็กต์ AuthGuard บน windowการเริ่มต้นใช้งาน
การตั้งค่าพื้นฐานร่วมกับ Router
// เริ่มต้น AuthManager ก่อน
await AuthManager.init({
enabled: true,
endpoints: {
login: '/api/auth/login',
verify: '/api/auth/verify'
}
});
// เริ่มต้น Router พร้อมเปิดใช้ AuthGuard
await Router.init({
routes: [
// เส้นทางสาธารณะ
{
path: '/',
handler: 'renderHome',
metadata: { public: true }
},
// เส้นทางที่ต้องล็อกอิน
{
path: '/dashboard',
handler: 'renderDashboard',
metadata: { requiresAuth: true }
}
],
// เปิดใช้ auth guard
auth: {
enabled: true,
guard: AuthGuard.checkRoute, // ใช้ AuthGuard
defaultRequireAuth: false,
redirects: {
unauthorized: '/login',
forbidden: '/403'
}
}
});
console.log('เริ่มต้น Router พร้อม AuthGuard แล้ว!');ลำดับการทำงานของ Guard
ผู้ใช้นำทางไปยังเส้นทาง
↓
Router เรียก AuthGuard.checkRoute()
↓
┌───────────────────────┐
│ ตรวจสอบประเภทเส้นทาง │
│ - เป็นเส้นทางสาธารณะหรือไม่? │
│ - สำหรับผู้เยี่ยมชมเท่านั้นหรือไม่? │
│ - เป็นเส้นทางที่ต้องล็อกอินหรือไม่? │
└──────────┬────────────┘
↓
┌───────────────────────┐
│ ดึงสถานะการยืนยันตัวตน │
│ - ตรวจสอบสถานะในเครื่อง │
│ - ยืนยันกับเซิร์ฟเวอร์ │
│ (เมื่อจำเป็น) │
└──────────┬────────────┘
↓
┌───────────────────────┐
│ ตรวจสอบเงื่อนไข │
│ - การยืนยันตัวตน │
│ - บทบาท │
│ - สิทธิ์ │
│ - การตรวจสอบเพิ่มเติม │
└──────────┬────────────┘
↓
┌────┴────┐
│ ผลลัพธ์ │
└────┬────┘
↓
┌─────┴─────┐
│ อนุญาตหรือไม่? │
└─────┬─────┘
Yes │ No
↓ ↓
นำทางต่อ จัดการข้อผิดพลาด
(เปลี่ยนเส้นทาง/บล็อก)การป้องกันเส้นทาง
1. เส้นทางสาธารณะ
// ใครก็เข้าถึงได้
{
path: '/',
handler: 'renderHome',
metadata: {
public: true // ไม่ต้องยืนยันตัวตน
}
}
// ตัวอย่างกำหนดหลายเส้นทางสาธารณะ
const publicRoutes = [
{ path: '/', handler: 'home', metadata: { public: true } },
{ path: '/about', handler: 'about', metadata: { public: true } },
{ path: '/contact', handler: 'contact', metadata: { public: true } }
];2. เส้นทางที่ต้องล็อกอิน
// ต้องยืนยันตัวตน
{
path: '/dashboard',
handler: 'renderDashboard',
metadata: {
requiresAuth: true // ต้องล็อกอินก่อน
}
}
// เส้นทางที่ล็อกอินพร้อม redirect เฉพาะ
{
path: '/profile',
handler: 'renderProfile',
metadata: {
requiresAuth: true,
redirectUnauthorized: '/login' // เปลี่ยนเส้นทางเมื่อไม่ได้รับอนุญาต
}
}3. เส้นทางสำหรับผู้เยี่ยมชม (Guest-Only Routes)
// ใช้ได้เฉพาะผู้ที่ยังไม่ล็อกอิน (Only for non-authenticated users)
{
path: '/login',
handler: 'renderLogin',
metadata: {
guestOnly: true, // สำหรับผู้เยี่ยมชมเท่านั้น
redirectOnAuth: '/dashboard' // เปลี่ยนเส้นทางเมื่อผู้ใช้ล็อกอินแล้ว
}
}
// ตัวอย่างเส้นทางสำหรับผู้เยี่ยมชม
const guestRoutes = [
{
path: '/login',
handler: 'login',
metadata: {
guestOnly: true,
redirectOnAuth: '/dashboard'
}
},
{
path: '/register',
handler: 'register',
metadata: {
guestOnly: true,
redirectOnAuth: '/dashboard'
}
}
];4. กำหนดการล็อกอินเป็นค่าเริ่มต้น
// ตั้งค่าเริ่มต้นสำหรับทุกเส้นทาง
await Router.init({
routes: [
{ path: '/', handler: 'home' }, // ต้องล็อกอินเป็นค่าเริ่มต้น
{ path: '/about', handler: 'about' }, // ต้องล็อกอินเป็นค่าเริ่มต้น
{
path: '/login',
handler: 'login',
metadata: { public: true } // กำหนดให้เป็นสาธารณะ (Override: public)
}
],
auth: {
enabled: true,
guard: AuthGuard.checkRoute,
defaultRequireAuth: true // ทุกเส้นทางต้องล็อกอินเป็นค่าเริ่มต้น
}
});การควบคุมการเข้าถึงตามบทบาท
1. ต้องมีบทบาทเดียว
// ให้เฉพาะผู้ดูแล (admin) เข้าถึง
{
path: '/admin',
handler: 'renderAdmin',
metadata: {
requiresAuth: true,
roles: ['admin'] // ต้องมีบทบาท admin
}
}2. หลายบทบาท (ตรรกะ OR)
// ให้ admin หรือ moderator เข้าถึง
{
path: '/moderation',
handler: 'renderModeration',
metadata: {
requiresAuth: true,
roles: ['admin', 'moderator'] // มีบทบาทใดบทบาทหนึ่งก็พอ
}
}3. ลำดับชั้นของบทบาท
// กำหนดลำดับบทบาทใน AuthManager
const user = {
id: 1,
name: 'John Doe',
roles: ['admin'] // admin มีสิทธิ์ทั้งหมด
};
// เส้นทางสำหรับ moderator และ admin
{
path: '/posts/moderate',
handler: 'moderatePosts',
metadata: {
requiresAuth: true,
roles: ['moderator', 'admin']
}
}
// เส้นทางสำหรับ admin เท่านั้น
{
path: '/users/manage',
handler: 'manageUsers',
metadata: {
requiresAuth: true,
roles: ['admin']
}
}4. ตรวจสอบบทบาทแบบไดนามิก
// ตรวจสอบบทบาทผู้ใช้ภายในการตรวจสอบแบบกำหนดเอง
{
path: '/content/:id/edit',
handler: 'editContent',
metadata: {
requiresAuth: true,
validate: async (to, from, router) => {
const user = AuthManager.getUser();
const contentId = to.params.id;
// ดึงข้อมูลคอนเทนต์เพื่อตรวจสอบความเป็นเจ้าของ
const response = await fetch(`/api/content/${contentId}`);
const content = await response.json();
// อนุญาตเมื่อ:
// - ผู้ใช้มีบทบาท admin
// - หรือผู้ใช้เป็นเจ้าของข้อมูล
const isAdmin = user.roles.includes('admin');
const isOwner = content.userId === user.id;
if (!isAdmin && !isOwner) {
return {
allowed: false,
// เส้นทางสาธารณะ
target: '/403',
reason: 'ไม่ใช่เจ้าของหรือผู้ดูแลระบบ'
};
}
return { allowed: true };
// เส้นทางสำหรับผู้เยี่ยมชม (Guest-only routes)
}
}การตรวจสอบสิทธิ์
1. สิทธิ์เดียว
// ต้องมีสิทธิ์เฉพาะ
{
path: '/posts/create',
handler: 'createPost',
metadata: {
requiresAuth: true,
// เส้นทางที่ต้องล็อกอิน
}
}2. หลายสิทธิ์ (ตรรกะ AND)
// ต้องมีสิทธิ์ครบทุกตัว
{
path: '/posts/:id/delete',
handler: 'deletePost',
metadata: {
requiresAuth: true,
permissions: ['read:posts', 'delete:posts'] // ต้องมีทั้งสองสิทธิ์
}
}3. สิทธิ์แบบละเอียด
// นิยามสิทธิ์แบบละเอียด
const routes = [
{
path: '/posts',
handler: 'listPosts',
metadata: {
requiresAuth: true,
permissions: ['read:posts']
}
},
{
path: '/posts/create',
handler: 'createPost',
metadata: {
requiresAuth: true,
permissions: ['write:posts']
}
},
{
path: '/posts/:id/edit',
handler: 'editPost',
metadata: {
requiresAuth: true,
permissions: ['read:posts', 'write:posts']
}
},
{
path: '/posts/:id/delete',
handler: 'deletePost',
metadata: {
requiresAuth: true,
permissions: ['delete:posts']
}
}
];4. สิทธิ์เฉพาะทรัพยากร (Resource-Specific Permissions)
// รูปแบบสิทธิ์: action:resource:id
{
path: '/projects/:projectId/edit',
handler: 'editProject',
metadata: {
requiresAuth: true,
validate: async (to, from, router) => {
const user = AuthManager.getUser();
const projectId = to.params.projectId;
// ตรวจสอบว่าผู้ใช้มีสิทธิ์ของโปรเจ็กต์นี้หรือไม่
const hasPermission = user.permissions.some(perm => {
// ตรวจสอบสิทธิ์เหล่านี้:
// - write:projects:* (ครอบคลุมทุกโปรเจ็กต์)
// - write:projects:123 (เฉพาะโปรเจ็กต์ที่ระบุ)
return perm === 'write:projects:*' ||
perm === `write:projects:${projectId}`;
});
if (!hasPermission) {
return {
allowed: false,
action: 'redirect',
target: '/403',
reason: 'ไม่มีสิทธิ์สำหรับโปรเจ็กต์นี้'
};
}
return { allowed: true };
}
}
}การตรวจสอบแบบกำหนดเอง
1. การตรวจสอบแบบอะซิงก์
{
path: '/premium/content',
handler: 'premiumContent',
metadata: {
requiresAuth: true,
validate: async (to, from, router) => {
const user = AuthManager.getUser();
// ตรวจสอบสถานะการสมัครสมาชิก (แบบอะซิงก์)
const response = await fetch('/api/subscription/check');
const { active } = await response.json();
if (!active) {
return {
allowed: false,
action: 'redirect',
target: '/subscribe',
reason: 'จำเป็นต้องมีการสมัครสมาชิก'
};
}
return { allowed: true };
}
}
}2. การตรวจสอบพร้อมบริบท
{
path: '/admin/reports/:reportId',
handler: 'viewReport',
metadata: {
requiresAuth: true,
roles: ['admin', 'manager'],
validate: async (to, from, router) => {
const user = AuthManager.getUser();
const reportId = to.params.reportId;
// ผู้จัดการดูได้เฉพาะรายงานของแผนกตัวเอง
if (user.roles.includes('manager')) {
const response = await fetch(`/api/reports/${reportId}`);
const report = await response.json();
if (report.department !== user.department) {
return {
allowed: false,
action: 'redirect',
target: '/403',
reason: 'รายงานนี้ไม่ได้อยู่ในแผนกของคุณ'
};
}
}
// ผู้ดูแล (admins) ดูได้ทุกแผนก
return { allowed: true };
}
}
}3. การตรวจสอบร่วมกับการจำกัดความถี่
{
path: '/api/export',
handler: 'exportData',
metadata: {
requiresAuth: true,
validate: async (to, from, router) => {
const user = AuthManager.getUser();
// ตรวจสอบโควต้าการใช้งาน
const response = await fetch('/api/rate-limit/check', {
method: 'POST',
body: JSON.stringify({ userId: user.id, action: 'export' })
});
const { allowed, remaining, resetAt } = await response.json();
if (!allowed) {
return {
allowed: false,
action: 'block',
reason: `เกินโควต้าการใช้งานแล้ว โปรดลองใหม่เวลา ${new Date(resetAt)}`
};
}
return {
allowed: true,
metadata: { remaining } // ส่งค่าที่เหลือให้ handler
};
}
}
}4. การเข้าถึงตามช่วงเวลา (Time-Based Access)
{
path: '/admin/maintenance',
handler: 'maintenance',
metadata: {
requiresAuth: true,
roles: ['admin'],
validate: async (to, from, router) => {
const now = new Date();
const hour = now.getHours();
// เข้าได้เฉพาะช่วงบำรุงรักษา (02:00 - 04:00)
if (hour < 2 || hour >= 4) {
return {
allowed: false,
action: 'block',
reason: 'โหมดบำรุงรักษาใช้งานได้เฉพาะเวลา 02:00 - 04:00 น.'
};
}
return { allowed: true };
}
}
}ผลลัพธ์ของ Guard
โครงสร้างผลลัพธ์
// ผลลัพธ์เมื่ออนุญาต
{
allowed: true,
reason: 'ตรวจสอบผ่านทุกเงื่อนไข',
metadata: {} // ข้อมูลเสริม
}
// ผลลัพธ์เมื่อถูกปฏิเสธ
{
allowed: false,
action: 'redirect', // ค่าที่รองรับ: 'redirect', 'block', 'render'
target: '/login', // เส้นทางปลายทางเมื่อ redirect/render
reason: 'ยังไม่ได้ยืนยันตัวตน',
error: Error // อ็อบเจ็กต์ข้อผิดพลาด
}ประเภทการดำเนินการ
1. Redirect Action
// เปลี่ยนเส้นทางไปยัง route อื่น
{
allowed: false,
action: 'redirect',
target: '/login',
reason: 'ต้องยืนยันตัวตน'
}2. Block Action
// บล็อกการนำทาง (คงอยู่หน้าปัจจุบัน)
{
allowed: false,
action: 'block',
reason: 'สิทธิ์ไม่เพียงพอ'
}3. Render Action
// แสดงหน้าข้อผิดพลาด
{
allowed: false,
action: 'render',
target: '/403',
reason: 'ไม่มีสิทธิ์เข้าถึง'
}การจัดการข้อผิดพลาด
1. ทำงานร่วมกับ AuthErrorHandler
// AuthGuard เรียกใช้ AuthErrorHandler อัตโนมัติ
// เมื่อการตรวจสอบ guard ล้มเหลว
// เมื่อ guard ล้มเหลวจะเรียก error handler
const guardResult = await AuthGuard.checkRoute(to, from, router);
if (!guardResult.allowed) {
// AuthErrorHandler จัดการสถานการณ์ผิดพลาด
await AuthGuard.handleGuardFailure(guardResult, route, options, router);
}2. การจัดการข้อผิดพลาดกำหนดเอง
// ฟังเหตุการณ์ guard ผิดพลาด
document.addEventListener('auth:guard:failed', (e) => {
const { route, reason, action } = e.detail;
console.log('Guard ล้มเหลวสำหรับเส้นทาง:', route.path);
console.log('สาเหตุ:', reason);
console.log('การดำเนินการ:', action);
// จัดการเพิ่มเติมตามต้องการ
if (reason.includes('subscription')) {
showSubscriptionModal();
}
});3. ประเภทของข้อผิดพลาด
// ประเภทข้อผิดพลาดที่เกิดจาก guard ล้มเหลว
// ข้อผิดพลาดด้านการยืนยันตัวตน
{
allowed: false,
action: 'redirect',
target: '/login',
reason: 'ยังไม่ได้ยืนยันตัวตน',
errorType: 'UNAUTHORIZED'
}
// ข้อผิดพลาดด้านสิทธิ์
{
allowed: false,
action: 'redirect',
target: '/403',
reason: 'ไม่มีบทบาทที่ต้องใช้: admin',
errorType: 'FORBIDDEN'
}
// ข้อผิดพลาดจากการตรวจสอบกำหนดเอง
{
allowed: false,
action: 'block',
reason: 'การสมัครสมาชิกหมดอายุ',
errorType: 'CUSTOM_VALIDATION'
}ตัวอย่างการใช้งาน
1. ตั้งค่า Router แบบครบถ้วนร่วมกับ AuthGuard
// เริ่มระบบยืนยันตัวตน
await AuthManager.init({
enabled: true,
endpoints: {
login: '/api/auth/login',
logout: '/api/auth/logout',
verify: '/api/auth/verify'
},
redirects: {
afterLogin: '/dashboard',
afterLogout: '/login'
}
});
// กำหนดเส้นทางพร้อมการป้องกัน
await Router.init({
routes: [
// เส้นทางสาธารณะ
{
path: '/',
handler: 'renderHome',
metadata: { public: true }
},
{
path: '/about',
handler: 'renderAbout',
metadata: { public: true }
},
// เส้นทางสำหรับผู้เยี่ยมชม (Guest-only routes)
{
path: '/login',
handler: 'renderLogin',
metadata: {
guestOnly: true,
redirectOnAuth: '/dashboard'
}
},
{
path: '/register',
handler: 'renderRegister',
metadata: {
guestOnly: true,
redirectOnAuth: '/dashboard'
}
},
// เส้นทางที่ต้องล็อกอิน
{
path: '/dashboard',
handler: 'renderDashboard',
metadata: {
requiresAuth: true
}
},
{
path: '/profile',
handler: 'renderProfile',
metadata: {
requiresAuth: true
}
},
// เส้นทางที่แยกตามบทบาท (Role-based routes)
{
path: '/admin',
handler: 'renderAdmin',
metadata: {
requiresAuth: true,
roles: ['admin']
}
},
{
path: '/moderation',
handler: 'renderModeration',
metadata: {
requiresAuth: true,
roles: ['admin', 'moderator']
}
},
// เส้นทางที่แยกตามสิทธิ์ (Permission-based routes)
{
path: '/posts/create',
handler: 'createPost',
metadata: {
requiresAuth: true,
permissions: ['write:posts']
}
},
{
path: '/posts/:id/delete',
handler: 'deletePost',
metadata: {
requiresAuth: true,
permissions: ['delete:posts']
}
}
],
// เปิดใช้งาน AuthGuard
auth: {
enabled: true,
guard: AuthGuard.checkRoute,
defaultRequireAuth: false,
redirects: {
unauthorized: '/login',
forbidden: '/403'
},
skipPatterns: [
'/api/*',
'/assets/*',
'*.js',
'*.css'
]
}
});
console.log('เริ่มต้น Router พร้อม AuthGuard แล้ว!');2. แดชบอร์ดตามบทบาท (Role-Based Dashboard)
// กำหนดเส้นทางตามบทบาทผู้ใช้
const routes = [
// แดชบอร์ดสำหรับผู้ใช้งานทั่วไป
{
path: '/dashboard/user',
handler: async () => {
const user = AuthManager.getUser();
return `
<div>
<h1>แดชบอร์ดผู้ใช้งาน</h1>
<p>ยินดีต้อนรับ, ${user.name}!</p>
</div>
`;
},
metadata: {
requiresAuth: true,
roles: ['user', 'moderator', 'admin']
}
},
// แดชบอร์ดสำหรับ moderator
{
path: '/dashboard/moderator',
handler: async () => {
return `
<div>
<h1>แดชบอร์ดผู้ดูแลเนื้อหา</h1>
<div id="modTools">...</div>
</div>
`;
},
metadata: {
requiresAuth: true,
roles: ['moderator', 'admin']
}
},
// แดชบอร์ดสำหรับ admin
{
path: '/dashboard/admin',
handler: async () => {
return `
<div>
<h1>แดชบอร์ดผู้ดูแลระบบ</h1>
<div id="adminPanel">...</div>
</div>
`;
},
metadata: {
requiresAuth: true,
roles: ['admin']
}
}
];
// นำทางตามบทบาทของผู้ใช้
async function goToDashboard() {
const user = AuthManager.getUser();
if (user.roles.includes('admin')) {
await Router.navigate('/dashboard/admin');
} else if (user.roles.includes('moderator')) {
await Router.navigate('/dashboard/moderator');
} else {
await Router.navigate('/dashboard/user');
}
}3. ตรวจสอบความเป็นเจ้าของคอนเทนต์
{
path: '/posts/:id/edit',
handler: 'editPost',
metadata: {
requiresAuth: true,
validate: async (to, from, router) => {
const user = AuthManager.getUser();
const postId = to.params.id;
// ดึงข้อมูลโพสต์
const response = await fetch(`/api/posts/${postId}`);
if (!response.ok) {
return {
allowed: false,
action: 'redirect',
target: '/404',
reason: 'ไม่พบโพสต์'
};
}
const post = await response.json();
// ตรวจสอบว่าเป็นเจ้าของหรือเป็น admin
const isOwner = post.userId === user.id;
const isAdmin = user.roles.includes('admin');
if (!isOwner && !isAdmin) {
return {
allowed: false,
action: 'redirect',
target: '/403',
reason: 'คุณสามารถแก้ไขโพสต์ของตัวเองเท่านั้น'
};
}
return {
allowed: true,
metadata: { post } // ส่งโพสต์ให้ handler ใช้ต่อ
};
}
}
}4. การเข้าถึงตามสถานะการสมัคร (Subscription-Based Access)
{
path: '/premium/*',
handler: 'premiumContent',
metadata: {
requiresAuth: true,
validate: async (to, from, router) => {
const user = AuthManager.getUser();
// ตรวจสอบสถานะการสมัครสมาชิก
const response = await fetch('/api/subscription/status');
const subscription = await response.json();
if (!subscription || subscription.status !== 'active') {
return {
allowed: false,
action: 'redirect',
target: '/subscribe',
reason: 'ต้องมีการสมัครสมาชิกที่ยังใช้งานอยู่'
};
}
// ตรวจสอบระดับการสมัครสมาชิกที่ต้องใช้เนื้อหานี้
const requiredLevel = 'premium';
if (subscription.level !== requiredLevel) {
return {
allowed: false,
action: 'redirect',
target: '/upgrade',
reason: `ต้องมีการสมัครสมาชิกระดับ ${requiredLevel}`
};
}
return {
allowed: true,
metadata: { subscription }
};
}
}
}5. ตรวจสอบการยืนยันหลายปัจจัย (Multi-Factor Authentication Check)
{
path: '/settings/security',
handler: 'securitySettings',
metadata: {
requiresAuth: true,
validate: async (to, from, router) => {
const user = AuthManager.getUser();
// ตรวจสอบว่าต้องทำ MFA หรือไม่
if (user.mfaEnabled && !user.mfaVerified) {
// เก็บเส้นทางที่ตั้งใจไว้
AuthGuard.storeIntendedRoute(to, router);
return {
allowed: false,
action: 'redirect',
target: '/mfa/verify',
reason: 'จำเป็นต้องยืนยันตัวตนแบบหลายปัจจัย'
};
}
return { allowed: true };
}
}
}6. ตรวจสอบรายการ IP ที่อนุญาต
{
path: '/admin/sensitive',
handler: 'sensitivePage',
metadata: {
requiresAuth: true,
roles: ['admin'],
validate: async (to, from, router) => {
// ตรวจสอบ IP ที่อนุญาต
const response = await fetch('/api/security/check-ip');
const { allowed, ip } = await response.json();
if (!allowed) {
// บันทึกเหตุการณ์ความปลอดภัย
await fetch('/api/security/log', {
method: 'POST',
body: JSON.stringify({
event: 'UNAUTHORIZED_IP_ACCESS',
ip,
path: to.path,
userId: AuthManager.getUser().id
})
});
return {
allowed: false,
action: 'block',
reason: 'ไม่อนุญาตให้เข้าถึงจาก IP นี้'
};
}
return { allowed: true };
}
}
}เอกสารอ้างอิง API
เมธอด
checkRoute(to, from, router)
ฟังก์ชันหลักของ guard ใช้ตรวจสอบสิทธิ์ก่อนการนำทาง
พารามิเตอร์:
to(Object) - เส้นทางปลายทางที่ต้องการไปfrom(Object) - เส้นทางปัจจุบันrouter(Object) - อินสแตนซ์ของ Router
ค่าที่ส่งกลับ: Promise<Object> - ผลลัพธ์การตรวจสอบ guard
ตัวอย่าง:
const result = await AuthGuard.checkRoute(toRoute, fromRoute, routerInstance);
if (result.allowed) {
console.log('อนุญาตให้นำทาง');
} else {
console.log('ปฏิเสธการนำทาง:', result.reason);
}getAuthState(authManager)
Get current authentication state with server verification
Parameters:
authManager(Object) - AuthManager instance
Returns: Promise<Object> - Auth state
Example:
const authState = await AuthGuard.getAuthState(AuthManager);
console.log('สถานะการยืนยันตัวตน:', authState.isAuthenticated);
console.log('ผู้ใช้:', authState.user);checkRoles(requiredRoles, userRoles)
Check if user has required roles
Parameters:
requiredRoles(Array) - Required rolesuserRoles(Array) - User's roles
Returns: boolean - True if user has any required role
Example:
const hasRole = AuthGuard.checkRoles(['admin', 'moderator'], user.roles);checkPermissions(requiredPermissions, userPermissions)
Check if user has required permissions
Parameters:
requiredPermissions(Array) - Required permissionsuserPermissions(Array) - User's permissions
Returns: boolean - True if user has all required permissions
Example:
const hasPerms = AuthGuard.checkPermissions(
['read:posts', 'write:posts'],
user.permissions
);storeIntendedRoute(route, router)
Store intended route for redirect after login
Parameters:
route(Object) - Route objectrouter(Object) - Router instance
Returns: void
Example:
AuthGuard.storeIntendedRoute(toRoute, router);getIntendedRoute()
Get and clear stored intended route
Returns: string|null - Route path or null
Example:
const intendedRoute = AuthGuard.getIntendedRoute();
if (intendedRoute) {
Router.navigate(intendedRoute);
}clearCache()
Clear authentication state cache
Returns: void
Example:
AuthGuard.clearCache();แนวทางปฏิบัติที่ดี
1. ระบุเส้นทางสาธารณะให้ชัดเจน
// ✅ ดี - ระบุเส้นทางสาธารณะชัดเจน
{
path: '/',
handler: 'home',
metadata: { public: true }
}
// ❌ ไม่ดี - การป้องกันไม่ชัดเจน
{
path: '/',
handler: 'home'
// ตกลงว่าเส้นทางนี้ป้องกันหรือสาธารณะ?
}2. ใช้ลำดับชั้นบทบาท
// ✅ ดี - ลำดับบทบาทชัดเจน
const routes = [
{
path: '/content/view',
metadata: { roles: ['user', 'moderator', 'admin'] }
},
{
path: '/content/moderate',
metadata: { roles: ['moderator', 'admin'] }
},
{
path: '/content/admin',
metadata: { roles: ['admin'] }
}
];
// ❌ ไม่ดี - ลำดับบทบาทไม่ชัดเจน
{
path: '/admin',
metadata: { roles: ['user'] } // ผู้ใช้ทั่วไปไม่ควรเข้าถึงหน้า admin!
}3. ใช้บทบาทร่วมกับการตรวจสอบกำหนดเอง
// ✅ ดี - ใช้บทบาทร่วมกับการตรวจสอบกำหนดเอง (Roles + validation)
{
path: '/posts/:id/edit',
metadata: {
requiresAuth: true,
roles: ['admin', 'author'],
validate: async (to) => {
// ผู้เขียนแก้ไขได้เฉพาะโพสต์ของตนเอง
const user = AuthManager.getUser();
if (user.roles.includes('author')) {
const post = await fetchPost(to.params.id);
return {
allowed: post.authorId === user.id,
reason: 'แก้ไขได้เฉพาะโพสต์ของตัวเอง'
};
}
// ผู้ดูแลแก้ไขโพสต์ใดก็ได้
return { allowed: true };
}
}
}4. แคชสถานะการล็อกอินอย่างเหมาะสม
// ✅ ดี - ตรวจสอบสิทธิ์ตามรอบเวลา
await Router.init({
auth: {
enabled: true,
guard: AuthGuard.checkRoute,
verifyInterval: 5 * 60 * 1000 // ตรวจสอบทุก ๆ 5 นาที
}
});
// ❌ ไม่ดี - ตรวจสอบทุกครั้งที่นำทาง
// จะเกิดขึ้นโดยอัตโนมัติหากไม่แคชสถานะ5. จัดการความล้มเหลวของ Guard อย่างเป็นมิตร
// ✅ ดี - แจ้งผู้ใช้ด้วยข้อความที่เข้าใจง่าย
document.addEventListener('auth:guard:failed', (e) => {
const { reason, action } = e.detail;
if (action === 'redirect') {
showNotification(`กำลังเปลี่ยนเส้นทาง: ${reason}`, 'info');
} else {
showNotification(reason, 'error');
}
});
// ❌ ไม่ดี - ปล่อยให้ล้มเหลวแบบเงียบ ๆ
// ผู้ใช้ไม่รู้สาเหตุว่าทำไมถึงนำทางไม่ได้ข้อผิดพลาดที่พบบ่อย
1. ลืมตั้งค่า requiresAuth
// ❌ ไม่ดี - ไม่ระบุว่าเส้นทางต้องล็อกอิน
{
path: '/dashboard',
handler: 'dashboard',
metadata: {
roles: ['user'] // ถ้าไม่ตั้ง requiresAuth จะไม่ตรวจบทบาท!
}
}
// ✅ ดี - ระบุว่าต้องล็อกอินให้ชัดเจน
{
path: '/dashboard',
handler: 'dashboard',
metadata: {
requiresAuth: true,
roles: ['user']
}
}2. สับสนระหว่างบทบาทกับสิทธิ์
// ❌ ไม่ดี - ใช้บทบาทกับสิทธิ์สลับกัน
{
path: '/posts/delete',
metadata: {
roles: ['delete:posts'] // นี่คือ permission ไม่ใช่ role!
}
}
// ✅ ดี - ใช้อย่างถูกต้อง
{
path: '/posts/delete',
metadata: {
requiresAuth: true,
permissions: ['delete:posts'] // ถูกต้อง
}
}3. ไม่จัดการข้อผิดพลาดจากการตรวจสอบกำหนดเอง
// ❌ ไม่ดี - การตรวจสอบไม่มีการจัดการข้อผิดพลาด
{
metadata: {
validate: async (to) => {
const response = await fetch('/api/check'); // อาจเกิด error ได้!
return { allowed: response.ok };
}
}
}
// ✅ ดี - จัดการข้อผิดพลาดให้ครบถ้วน
{
metadata: {
validate: async (to) => {
try {
const response = await fetch('/api/check');
const data = await response.json();
return { allowed: data.ok };
} catch (error) {
console.error('เกิดข้อผิดพลาดระหว่างการตรวจสอบ:', error);
return {
allowed: false,
reason: 'การตรวจสอบล้มเหลว'
};
}
}
}
}4. การ Redirect วนซ้ำ
// ❌ ไม่ดี - อาจเกิดลูปไม่รู้จบ
{
path: '/login',
metadata: {
requiresAuth: true // จะ redirect ไป /login หากยังไม่ล็อกอิน
}
}
// ✅ ดี - หน้าเข้าสู่ระบบสงวนให้ guest เท่านั้น (Login is guest-only)
{
path: '/login',
metadata: {
guestOnly: true,
redirectOnAuth: '/dashboard'
}
}ประสิทธิภาพและการปรับแต่ง
1. แคชสถานะการล็อกอิน
// ✅ AuthGuard แคชสถานะการล็อกอินให้อัตโนมัติ
// การตรวจสอบครั้งต่อไปใช้ข้อมูลแคช
await AuthGuard.checkRoute(route1, null, router);
await AuthGuard.checkRoute(route2, null, router); // ใช้ข้อมูลจากแคช
// การล้างแคชด้วยตนเอง
AuthGuard.clearCache(); // บังคับให้ตรวจสอบใหม่2. ลดจำนวนการเรียก API ในการตรวจสอบกำหนดเอง
// ❌ ไม่ดี - เรียก API ทุกครั้งที่นำทาง
{
metadata: {
validate: async (to) => {
const response = await fetch('/api/check-subscription');
return { allowed: response.ok };
}
}
}
// ✅ ดี - แคชผลการตรวจสอบ
let subscriptionCache = null;
let cacheTime = 0;
{
metadata: {
validate: async (to) => {
const now = Date.now();
if (!subscriptionCache || (now - cacheTime) > 60000) {
const response = await fetch('/api/check-subscription');
subscriptionCache = await response.json();
cacheTime = now;
}
return { allowed: subscriptionCache.active };
}
}
}3. ปรับปรุงการตรวจสอบบทบาท/สิทธิ์
// ✅ AuthGuard ปรับแต่งการตรวจสอบภายในไว้แล้ว
// - หยุดเมื่อเจอเงื่อนไขตรง (ตรรกะ OR สำหรับบทบาท)
// - แคชบทบาทและสิทธิ์ของผู้ใช้เอกสารที่เกี่ยวข้อง
- Authentication.md - ภาพรวมระบบการยืนยันตัวตน
- AuthManager.md - ตัวจัดการการยืนยันตัวตนหลัก
- TokenService.md - การจัดการโทเค็น JWT
- AuthErrorHandler.md - การจัดการข้อผิดพลาด
- AuthLoadingManager.md - การแสดงสถานะโหลดระหว่างการยืนยัน
- Router.md - ระบบกำหนดเส้นทาง