•7 min read

Next.js Middleware: Xây dựng xác thực và ủy quyền mạnh mẽ

Next.js Middleware: Xây dựng xác thực và ủy quyền mạnh mẽ

Xác thực và ủy quyền là nền tảng của bất kỳ ứng dụng web bảo mật nào. Trước đây, trong Next.js, việc bảo mật các tuyến đường có nghĩa là gói các thành phần trong Higher-Order Components (HOCs) hoặc thực hiện kiểm tra trong getServerSideProps trên cơ sở từng trang. Tuy nhiên, với sự trưởng thành của Next.js App Router và Edge Middleware, giờ đây chúng ta có một cách tiếp cận vượt trội hơn nhiều, tập trung và hiệu quả để xử lý bảo mật.

Vào năm 2026, Next.js Middleware chạy trên Edge, có nghĩa là các kiểm tra xác thực diễn ra cực kỳ gần với người dùng, trước khi một yêu cầu thậm chí còn đến máy chủ gốc của bạn. Bài viết này khám phá cách xây dựng các đường ống xác thực và ủy quyền mạnh mẽ, có khả năng mở rộng bằng cách sử dụng Next.js Middleware.

Audio Briefing
0:00 / 0:00

Tại sao lại dùng Middleware cho Xác thực?

Middleware cho phép bạn chặn một yêu cầu, kiểm tra một phiên hoặc mã thông báo hợp lệ và quyết định xem có cho phép yêu cầu đi qua, viết lại nó hay chuyển hướng người dùng hay không.

Lợi ích chính:

  1. Tập trung hóa: Bạn không còn cần phải phân tán logic xác thực trên hàng chục trang hoặc bố cục. Một tệp middleware.ts duy nhất có thể bảo mật toàn bộ ứng dụng của bạn.
  2. Hiệu suất: Chạy trên các môi trường thời gian chạy Edge (như Vercel Edge Functions hoặc Cloudflare Workers) có nghĩa là thời gian khởi động gần như bằng 0 và độ trễ tối thiểu. Việc kiểm tra diễn ra trước khi trang bắt đầu hiển thị.
  3. Bảo mật: Bằng cách chặn các yêu cầu chưa được xác thực tại Edge, bạn giảm tải cho máy chủ gốc và cơ sở dữ liệu của mình.
Advertisement

Luồng xác thực cơ bản

Kiểm tra và xem xét tải trọng JWT, tiêu đề yêu cầu và dấu thời gian hết hạn của bạn trong thời gian thực với bộ giải mã trình duyệt không rò rỉ của chúng tôi:

Hãy xem xét một triển khai tiêu chuẩn sử dụng JSON Web Tokens (JWTs) được lưu trữ trong cookie.

1. Thiết lập Middleware

Tạo một tệp middleware.ts tại thư mục gốc của dự án của bạn (hoặc trong thư mục src nếu bạn sử dụng nó).

import { NextResponse } from 'next/server'
import type { NextRequest } from 'next/server'
import { verifyJwtToken } from '@/lib/auth' // Your custom JWT verifier

export async function middleware(request: NextRequest) {
  const token = request.cookies.get('auth-token')?.value

  const isAuthPage = request.nextUrl.pathname.startsWith('/login') || 
                     request.nextUrl.pathname.startsWith('/register')

  // 1. Check if token exists and is valid
  const verifiedToken = token && (await verifyJwtToken(token).catch((err) => {
    console.error('Failed to verify token', err)
  }))

  // 2. Redirect unauthenticated users away from protected routes
  if (!verifiedToken) {
    if (!isAuthPage && request.nextUrl.pathname.startsWith('/dashboard')) {
      const loginUrl = new URL('/login', request.url)
      loginUrl.searchParams.set('callbackUrl', request.nextUrl.pathname)
      return NextResponse.redirect(loginUrl)
    }
  }

  // 3. Redirect authenticated users away from auth pages
  if (verifiedToken && isAuthPage) {
    return NextResponse.redirect(new URL('/dashboard', request.url))
  }

  // 4. Pass the request through if all checks pass
  return NextResponse.next()
}

// See "Matching Paths" below
export const config = {
  matcher: ['/dashboard/:path*', '/login', '/register'],
}

2. Khớp đường dẫn (matcher)

Cấu hình matcher rất quan trọng. Nó cho Next.js biết các tuyến đường nào sẽ kích hoạt middleware. Chạy middleware trên mọi yêu cầu (bao gồm tài sản tĩnh và tuyến API) là không hiệu quả.

Bằng cách chỉ định ['/dashboard/:path*', '/login', '/register'], chúng tôi đảm bảo logic xác thực của chúng tôi chỉ chạy khi cần thiết.

Xử lý ủy quyền (Kiểm soát truy cập dựa trên vai trò - RBAC)

Xác thực hỏi "Bạn là ai?". Ủy quyền hỏi "Bạn được phép làm gì?". Khi người dùng được xác thực, chúng ta thường cần kiểm tra vai trò hoặc quyền của họ.

Chúng ta có thể dễ dàng mở rộng middleware của mình để xử lý RBAC.

// ... previous imports
import { verifyJwtToken } from '@/lib/auth'

export async function middleware(request: NextRequest) {
  const token = request.cookies.get('auth-token')?.value
  const verifiedToken = token && (await verifyJwtToken(token))

  // ... standard auth checks

  // Handle Authorization
  if (verifiedToken) {
    const userRole = verifiedToken.role // Assuming role is encoded in the JWT

    // Protect admin routes
    if (request.nextUrl.pathname.startsWith('/admin')) {
      if (userRole !== 'ADMIN') {
        // User is authenticated, but not authorized
        return NextResponse.redirect(new URL('/unauthorized', request.url))
      }
    }
  }

  return NextResponse.next()
}

export const config = {
  matcher: ['/dashboard/:path*', '/admin/:path*', '/login'],
}

Các phương pháp bảo mật tốt nhất trong Middleware

  1. Giữ cho nó nhẹ nhàng: Các môi trường thời gian chạy Edge có giới hạn về kích thước và thời gian thực thi. Không thực hiện các truy vấn cơ sở dữ liệu nặng trong middleware của bạn. Dựa vào các kiểm tra nhanh, không trạng thái như xác minh JWT.
  2. Sử dụng jose cho JWT Edge: jsonwebtoken Node.js tiêu chuẩn dựa vào các API mã hóa Node, không có sẵn trên Edge. Sử dụng một thư viện như jose để ký và xác minh mã thông báo trong middleware.
  3. Cookie an toàn: Đảm bảo cookie xác thực của bạn được đặt với HttpOnly, Secure và SameSite=lax hoặc strict để ngăn chặn các cuộc tấn công XSS và CSRF.
  4. Xử lý lỗi duyên dáng: Nếu xác minh mã thông báo thất bại (ví dụ: chữ ký hết hạn), hãy bắt lỗi một cách duyên dáng và chuyển hướng đến đăng nhập, thay vì ném một ngoại lệ không được xử lý gây ra lỗi 500.
Advertisement

Nâng cao: Truyền dữ liệu đến các thành phần máy chủ

Một thách thức phổ biến là: sau khi xác minh mã thông báo trong middleware, làm thế nào chúng ta có thể truyền dữ liệu người dùng đến các Thành phần máy chủ mà không cần xác minh lại mã thông báo?

Bạn có thể thay đổi tiêu đề yêu cầu trong middleware và đọc chúng trong các Thành phần máy chủ của bạn.

Trong Middleware:

if (verifiedToken) {
  const requestHeaders = new Headers(request.headers)
  requestHeaders.set('x-user-id', verifiedToken.userId)
  
  return NextResponse.next({
    request: {
      headers: requestHeaders,
    },
  })
}

Trong Thành phần máy chủ (page.tsx):

import { headers } from 'next/headers'

export default function DashboardPage() {
  const headersList = headers()
  const userId = headersList.get('x-user-id')

  return <div>Welcome User: {userId}</div>
}

Kết luận

Next.js Middleware cung cấp một lớp mạnh mẽ, được tối ưu hóa cho Edge để xử lý xác thực và ủy quyền. Bằng cách tập trung các kiểm tra này, bạn đơn giản hóa đáng kể hệ thống phân cấp thành phần của mình, cải thiện hiệu suất và tăng cường tư thế bảo mật tổng thể của ứng dụng. Khi kết hợp với JWT không trạng thái và khớp tuyến đường cẩn thận, nó tạo thành xương sống của một kiến trúc Next.js hiện đại.

Bạn cũng có thể thích

Share this article:

Stay Updated

Get the latest posts delivered straight to your inbox.

Free Developer Utilities

Free In-Browser Developer Tools

Clean AI CLI logs, build cron expressions, decode JWTs, and calculate chmod permissions offline.

Explore Tools
Advertisement