
在 Next.js 14 App Router 中,直接在 useClient 布局组件中使用 localStorage + useEffect 进行身份验证会导致服务端渲染错误(localStorage is not defined)和无限重定向循环,需结合路由守卫、客户端检测及路径条件判断来安全实现。
在 next.js 14 app router 中,直接在 `useclient` 布局组件中使用 `localstorage` + `useeffect` 进行身份验证会导致服务端渲染错误(`localstorage is not defined`)和无限重定向循环,需结合路由守卫、客户端检测及路径条件判断来安全实现。
在 Next.js 14 的 App Router 架构下,app/(dashboard)/AppLayout.js 是一个 客户端组件('use client'),但它仍可能在服务端渲染(SSR)阶段被初始化——尤其当它被嵌套在根 RootLayout 中时。此时访问浏览器专属 API(如 localStorage)会立即抛出 ReferenceError: localStorage is not defined,这是根本性错误来源。
更关键的是,原始逻辑未做路径白名单校验:useEffect 在 /auth/login 页面也会执行,导致已进入登录页却因无 token 再次触发 redirect('/auth/login'),引发无限重定向循环——这不仅造成 UI 闪烁、多次渲染 AppLayout,还会快速耗尽调用栈或内存,最终使页面崩溃且控制台报错。
✅ 正确做法需同时满足三点:
- ✅ 客户端专属逻辑延迟执行:确保
localStorage仅在浏览器环境读取; - ✅ 路由守卫精准控制:对
/auth/login等免鉴权路径显式放行; - ✅ 双向守卫防绕过:既拦截未登录用户访问受保护路由,也阻止已登录用户重复访问登录页。
以下是修复后的 AppLayout.js 完整实现:
Miller (mlr) 是一个命令行工具,用于查询、整形和重新格式化名称索引数据,如 CSV、TSV、JSON 和 JSON Lines。它将 awk、sed、cut、join 和 sort 的功能整合到一个专为结构化数据处理而构建的单一工具中。
// app/(dashboard)/AppLayout.jsx
'use client';
import { redirect } from 'next/navigation';
import { useEffect, useState } from 'react';
import { usePathname } from 'next/navigation';
export default function Layout({ children }) {
const pathname = usePathname();
const [isClient, setIsClient] = useState(false);
const [token, setToken] = useState(null);
// ✅ 确保只在浏览器端执行 localStorage 操作
useEffect(() => {
setIsClient(true);
const storedToken = typeof window !== 'undefined'
? window.localStorage.getItem('token')
: null;
setToken(storedToken);
}, []);
// ✅ 双向路由守卫:依赖 isClient 和 pathname 精确判断
useEffect(() => {
if (!isClient) return;
// ? 未登录且不在登录页 → 强制跳转
if (!token && pathname !== '/auth/login') {
redirect('/auth/login');
return;
}
// ? 已登录但停留在登录页 → 重定向至首页(如 /dashboard)
if (token && pathname === '/auth/login') {
redirect('/dashboard'); // 或 '/home',根据你的默认入口调整
return;
}
}, [isClient, token, pathname]);
// ⚠️ SSR 期间暂不渲染内容,避免布局闪动或服务端错误
if (!isClient) {
return null; // 或可返回 skeleton / loading state
}
return (
<div classname="flex-1 min-h-screen">
{children}
</div>
);
}
? 关键注意事项:
-
永远不要在服务端上下文访问
localStorage:必须通过typeof window !== 'undefined'或useState + useEffect延迟到客户端; -
usePathname()是必需的:它是 App Router 提供的安全路由状态钩子,不可用window.location.pathname替代(SSR 不兼容); -
避免空
redirect()调用:每次redirect()都会中断当前渲染,务必用return阻止后续逻辑执行; -
登录页需独立于
(dashboard)组:你当前的app/auth/login/page.js结构正确(未被AppLayout包裹),确保其路径为/auth/login且不经过任何鉴权布局; -
生产环境建议升级守卫逻辑:将 token 校验与 JWT 解析、过期检查结合,并配合服务端 API 验证(如
getServerSession),防止客户端篡改 token。
最后,若需进一步提升安全性,可在 middleware.ts 中添加服务端鉴权(适用于所有请求),作为客户端守卫的补充层,形成前后端双重防护。










