Now.js Framework Documentation

Now.js Framework Documentation

AuthGuard - ระบบป้องกันเส้นทางและควบคุมการเข้าถึง

TH 31 Oct 2025 01:26

AuthGuard - ระบบป้องกันเส้นทางและควบคุมการเข้าถึง

เอกสารสำหรับ AuthGuard ซึ่งเป็นระบบป้องกันเส้นทาง และควบคุมการเข้าถึง ของ Now.js Framework

📋 สารบัญ

  1. ภาพรวม
  2. การติดตั้งและนำเข้า
  3. การเริ่มต้นใช้งาน
  4. การป้องกันเส้นทาง
  5. การควบคุมการเข้าถึงตามบทบาท
  6. การตรวจสอบสิทธิ์
  7. การตรวจสอบแบบกำหนดเอง
  8. ผลลัพธ์ของ Guard
  9. การจัดการข้อผิดพลาด
  10. ตัวอย่างการใช้งาน
  11. เอกสารอ้างอิง API
  12. แนวทางปฏิบัติที่ดี
  13. ข้อผิดพลาดที่พบบ่อย
  14. ประสิทธิภาพและการปรับแต่ง

ภาพรวม

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 roles
  • userRoles (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 permissions
  • userPermissions (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 object
  • router (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 - ระบบกำหนดเส้นทาง