Quản lý Quyền Truy Cập và Kiểm Soát Điều Hướng trong Vue Router

Vue Router cung cấp cơ chế kiểm soát luồng điều hướng thông qua các hàm gọi lại (guard) được kích hoạt ở các giai đoạn khác nhau của quá trình chuyển trang. Đây không chỉ là công cụ xác thực, mà còn là nền tảng để xây dựng logic phân quyền, tải dữ liệu có điều kiện, hoặc xử lý hành vi người dùng trước/sau khi điều hướng.

1. Các loại guard chính

Hệ thống guard được chia thành ba cấp độ: toàn cục (global), riêng lẻ theo route (route-specific), và gắn với thành phần (in-component). Mỗi loại đảm nhận vai trò khác nhau trong chuỗi điều hướng.

2. Guard toàn cục

Được khai báo trên đối tượng router, áp dụng cho mọi lần điều hướng. Có ba hàm chính:

  • router.beforeEach(): Chạy trước khi điều hướng bắt đầu. Nhận ba tham số: to (đối tượng route đích), from (route hiện tại), và next (hàm tiếp tục hoặc hủy điều hướng).
  • router.beforeResolve() (từ phiên bản 3.1+): Chạy sau khi các component liên quan đã được resolve (bao gồm cả async component), nhưng trước khi điều hướng được xác nhận.
  • router.afterEach(): Chạy sau khi điều hướng hoàn tất — không nhận tham số next, chỉ dùng để ghi log, cập nhật UI, hoặc tích hợp analytics.

Ví dụ minh họa việc kiểm tra phiên đăng nhập trước mỗi yêu cầu điều hướng:

import { createRouter, createWebHistory } from 'vue-router';

const routes = [
  { path: '/', redirect: '/dashboard' },
  { path: '/login', component: () => import('@/views/Login.vue') },
  {
    path: '/dashboard',
    component: () => import('@/views/Dashboard.vue'),
    children: [
      { path: '', redirect: 'overview' },
      { path: 'overview', component: () => import('@/views/Overview.vue') },
      { path: 'profile', component: () => import('@/views/Profile.vue') }
    ]
  }
];

const router = createRouter({
  history: createWebHistory(),
  routes
});

router.beforeEach((to, from, next) => {
  const isAuthenticated = !!localStorage.getItem('auth_token');
  
  if (to.path === '/login') {
    return next();
  }

  if (!isAuthenticated && to.path !== '/login') {
    return next({ path: '/login', query: { redirect: to.fullPath } });
  }

  next();
});

export default router;

3. Guard riêng lẻ theo route

Chỉ áp dụng cho một route cụ thể, khai báo trực tiếp trong cấu hình route dưới dạng thuộc tính beforeEnter. Nó được thực thi ngay sau beforeEach và trước beforeRouteEnter của component tương ứng.

Ví dụ: Chỉ yêu cầu xác thực khi truy cập vào khu vực quản trị:

const routes = [
  {
    path: '/admin',
    component: () => import('@/views/AdminPanel.vue'),
    beforeEnter: (to, from, next) => {
      if (!checkAdminPermission()) {
        next('/unauthorized');
      } else {
        next();
      }
    }
  }
];

4. Guard trong component

Các hàm này được định nghĩa bên trong component — tương tự như lifecycle hook, nhưng gắn với chu kỳ điều hướng:

  • beforeRouteEnter: Chạy trước khi component được khởi tạo. Không thể truy cập this, nhưng có thể truyền callback vào next() để nhận instance sau khi mounted.
  • beforeRouteUpdate (từ Vue Router 4): Kích hoạt khi route thay đổi nhưng component được tái sử dụng (ví dụ: từ /user/1 sang /user/2). Lúc này this đã khả dụng.
  • beforeRouteLeave: Chạy trước khi rời khỏi route hiện tại — hữu ích để xác nhận thoát khi có dữ liệu chưa lưu.

Một ví dụ thực tế cho beforeRouteEnter:

export default {
  name: 'UserProfile',
  setup() {
    const userData = ref(null);

    onBeforeRouteEnter((to, from, next) => {
      fetchUser(to.params.id).then(user => {
        userData.value = user;
        next();
      });
    });

    return { userData };
  }
};

5. Hành vi của tham số next

Tham số next quyết định luồng điều hướng. Các cách sử dụng phổ biến:

  • next(): Tiếp tục điều hướng bình thường.
  • next(false): Hủy bỏ điều hướng — URL sẽ quay lại trạng thái trước đó.
  • next('/path') hoặc next({ name: 'Home' }): Chuyển hướng đến một route khác.
  • next(error): Dừng điều hướng và truyền lỗi tới router.onError().

Lưu ý: Nếu quên gọi next() trong bất kỳ guard nào có tham số này, điều hướng sẽ bị treo vĩnh viễn.

6. Thứ tự thực thi

Khi điều hướng xảy ra, thứ tự chạy các guard và lifecycle như sau:

  1. beforeRouteLeave (nếu có trong component hiện tại)
  2. router.beforeEach
  3. beforeEnter (nếu có trong route đích)
  4. beforeRouteEnter (trong component đích)
  5. router.beforeResolve
  6. router.afterEach
  7. beforeCreatecreatedbeforeMountmounted
  8. Callback truyền vào next() trong beforeRouteEnter

Khi route thay đổi nhưng component được tái sử dụng (ví dụ: thay đổi tham số động hoặc query), chỉ beforeRouteUpdate được kích hoạt — không chạy lại các guard toàn cục hay lifecycle.

Thẻ: vue-router vue-3 frontend-security routing-guards authentication-flow

Đăng vào ngày 26 tháng 7 lúc 19:44