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

Next.js Layout层级JWT鉴权及localStorage读取问题求助

解决方案:Next.js 根Layout全局JWT权限控制

1. 修正客户端权限校验工具函数

你之前的IsAuth函数存在笔误(windows应为window),且需添加'use client'标记——因为要访问浏览器的localStorage,必须用客户端组件实现:

'use client';

import { useState, useEffect } from 'react';

export const useIsAuth = () => {
  const [isAuthenticated, setIsAuthenticated] = useState<boolean | null>(null);

  useEffect(() => {
    // 仅在浏览器环境执行
    if (typeof window === 'undefined') return;

    const jwtToken = localStorage.getItem('ltu');
    // 不仅判断token存在,还要验证有效性(推荐)
    const isValidToken = validateJwtToken(jwtToken);
    setIsAuthenticated(isValidToken);
  }, []);

  return isAuthenticated;
};

// 辅助函数:验证JWT有效性(解析payload+检查过期时间)
const validateJwtToken = (token: string | null): boolean => {
  if (!token) return false;
  try {
    const payload = JSON.parse(atob(token.split('.')[1]));
    // exp是JWT自带的过期时间戳(单位秒),转毫秒后对比当前时间
    return payload.exp * 1000 > Date.now();
  } catch (err) {
    return false;
  }
};

2. 全局权限守卫组件

编写客户端组件AuthGuard,统一处理路由权限判断,避免每个页面重复写校验逻辑:

'use client';

import { useEffect } from 'react';
import { useRouter, usePathname } from 'next/navigation';
import { useIsAuth } from './useIsAuth';

// 允许未登录访问的路由白名单
const PUBLIC_ROUTES = ['/', '/login', '/register'];

export const AuthGuard = ({ children }: { children: React.ReactNode }) => {
  const isAuthenticated = useIsAuth();
  const router = useRouter();
  const pathname = usePathname();

  useEffect(() => {
    // 等待权限状态加载完成再判断
    if (isAuthenticated === null) return;

    // 非白名单路由且未登录,跳转到登录页
    if (!isAuthenticated && !PUBLIC_ROUTES.includes(pathname)) {
      router.push('/login');
    }
    // 已登录但访问登录/注册页,自动跳转到首页(可选)
    if (isAuthenticated && PUBLIC_ROUTES.includes(pathname)) {
      router.push('/dashboard');
    }
  }, [isAuthenticated, pathname, router]);

  // 权限状态未加载完成时,可显示加载占位(可选)
  if (isAuthenticated === null) {
    return <div>加载中...</div>;
  }

  return children;
};

3. 在根Layout中挂载全局守卫

根Layout是所有页面的顶层容器,直接引入AuthGuard包裹所有页面内容,即可实现全局权限控制:

// app/layout.tsx(根Layout文件)
import { AuthGuard } from './components/AuthGuard';

export default function RootLayout({
  children,
}: {
  children: React.ReactNode;
}) {
  return (
    <html lang="zh-CN">
      <body>
        {/* 全局权限守卫,所有页面都会经过这里校验 */}
        <AuthGuard>{children}</AuthGuard>
      </body>
    </html>
  );
}

核心说明

  • Next.js 根Layout默认是服务器组件,无法访问浏览器API(如localStorage),因此权限校验逻辑必须放在标记'use client'的客户端组件中。
  • 通过根Layout挂载AuthGuard,无需在每个页面单独编写权限校验代码,实现统一控制。
  • JWT校验不能仅判断存在性,必须验证过期时间,避免使用无效/过期的token。

内容的提问来源于stack exchange,提问作者Skinsky Gnudy

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.31 08:56:06