You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Next.js 14客户端侧实现App Router受保护路由的JWT校验?

Next.js 14 App Router 实现JWT前置校验

完全可以实现页面访问前的JWT有效性校验,下面分两种场景给出具体实现方案:

场景1:客户端侧校验(适配localStorage存储令牌)

因为你的JWT存在localStorage(仅客户端可访问),所以可以通过客户端组件的路由守卫来处理:

方式1:封装全局校验组件

创建一个AuthGuard.tsx客户端组件,用于包裹需要校验的页面:

'use client';

import { useEffect } from 'react';
import { useRouter } from 'next/navigation';

const AuthGuard = ({ children }: { children: React.ReactNode }) => {
  const router = useRouter();

  useEffect(() => {
    const validateToken = async () => {
      const token = localStorage.getItem('jwt_token');
      if (!token) {
        router.push('/login');
        return;
      }

      // 调用后端接口校验令牌有效性
      try {
        const res = await fetch('/api/auth/validate', {
          method: 'POST',
          headers: {
            'Authorization': `Bearer ${token}`
          }
        });

        if (!res.ok) {
          localStorage.removeItem('jwt_token');
          router.push('/login');
        }
      } catch (err) {
        localStorage.removeItem('jwt_token');
        router.push('/login');
      }
    };

    validateToken();
  }, [router]);

  return <>{children}</>;
};

export default AuthGuard;

然后在需要保护的页面(客户端组件)中使用:

'use client';

import AuthGuard from '@/components/AuthGuard';

const DashboardPage = () => {
  return (
    <AuthGuard>
      {/* 页面内容 */}
      <h1>仪表盘</h1>
    </AuthGuard>
  );
};

export default DashboardPage;

方式2:使用layout.tsx全局校验

如果大部分页面都需要校验,可以在根layout.tsx(或子路由的layout.tsx)中加入校验逻辑,注意要标记为客户端组件:

'use client';

import { useEffect } from 'react';
import { useRouter } from 'next/navigation';

export default function RootLayout({
  children,
}: {
  children: React.ReactNode;
}) {
  const router = useRouter();

  useEffect(() => {
    const token = localStorage.getItem('jwt_token');
    // 排除登录页面,避免死循环
    const currentPath = window.location.pathname;
    if (!token && currentPath !== '/login') {
      router.push('/login');
      return;
    }

    if (token) {
      // 可选:校验令牌有效性
      fetch('/api/auth/validate', {
        method: 'POST',
        headers: { 'Authorization': `Bearer ${token}` }
      }).then(res => {
        if (!res.ok) {
          localStorage.removeItem('jwt_token');
          router.push('/login');
        }
      }).catch(() => {
        localStorage.removeItem('jwt_token');
        router.push('/login');
      });
    }
  }, [router]);

  return (
    <html lang="zh-CN">
      <body>{children}</body>
    </html>
  );
}

场景2:结合服务器端校验(优化方案)

如果希望在服务器端也做一层校验,建议把JWT同时存在HttpOnly的Cookie中(和localStorage并存),这样中间件可以读取Cookie中的令牌进行校验,避免客户端校验的延迟:

编写middleware.ts(根目录)

import { NextResponse } from 'next/server';
import type { NextRequest } from 'next/server';

export function middleware(request: NextRequest) {
  // 从Cookie获取令牌
  const token = request.cookies.get('jwt_token')?.value;
  const currentPath = request.nextUrl.pathname;

  // 排除登录页面和公开路由
  if (currentPath === '/login' || currentPath.startsWith('/public')) {
    return NextResponse.next();
  }

  // 无令牌则重定向到登录页
  if (!token) {
    return NextResponse.redirect(new URL('/login', request.url));
  }

  // 可选:调用后端接口校验令牌有效性(服务器端请求)
  // const res = await fetch(process.env.NEXT_PUBLIC_API_URL + '/auth/validate', {
  //   headers: { 'Authorization': `Bearer ${token}` }
  // });
  // if (!res.ok) {
  //   return NextResponse.redirect(new URL('/login', request.url));
  // }

  return NextResponse.next();
}

// 指定需要校验的路由
export const config = {
  matcher: ['/dashboard/:path*', '/profile/:path*'], // 按需配置
};

注意事项

  • 客户端校验时,页面会短暂渲染后再跳转,若要避免这种情况,可以在校验完成前显示加载态
  • 令牌校验接口要返回明确的状态码(比如401表示令牌无效)
  • 若使用服务器端中间件,务必确保令牌存储在Cookie中(HttpOnly属性可以防止XSS攻击)

内容的提问来源于stack exchange,提问作者Muhammad Talha

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.01 07:35:56