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

Next.js 13如何使用LocalStorage实现路由守卫?

问题解决与方案汇总

核心问题原因

你当前的代码失效是因为Next.js Middleware运行在服务端(Edge Runtime),而localStorage是浏览器专属API,服务端无法读取浏览器的LocalStorage数据。所以不管用户是否登录,getUserFromLocalStorage()都会返回null,导致始终重定向到登录页。

立即修复方案:改用Cookie存储认证信息

服务端Middleware可以读取请求头里的Cookie,这是服务端校验登录状态的标准方式:

  1. 修改登录逻辑:登录成功后,将用户信息(或简化的token)存入Cookie,而非LocalStorage:
// 登录成功后执行
document.cookie = `user=${JSON.stringify(userData)}; path=/; max-age=86400; SameSite=Lax`;
// 可选:同时存入LocalStorage做客户端缓存
localStorage.setItem('user', JSON.stringify(userData));
  1. 修正Middleware代码:从请求Cookie中读取用户信息:
// middleware.ts
import { NextResponse } from 'next/server'
import type { NextRequest } from 'next/server'

export async function middleware(request: NextRequest) {
    // 从Cookie中读取用户信息
    const userCookie = request.cookies.get('user')?.value;
    const user = userCookie ? JSON.parse(userCookie) : null;

    // 排除登录页,避免死循环
    if (!user && request.nextUrl.pathname !== '/login') {
        return NextResponse.redirect(new URL('/login', request.url))
    }
    // 已登录用户访问登录页时,重定向到主页
    if (user && request.nextUrl.pathname === '/login') {
        return NextResponse.redirect(new URL('/', request.url))
    }
}

export const config = {
    // 匹配所有需要授权的路由,可根据需求调整
    matcher: ['/', '/dashboard/:path*', '/profile/:path*']
}

LocalStorage/IndexedDB实现路由守卫的常用方案

这类存储属于客户端缓存,适合做客户端路由守卫,搭配服务端校验使用:

方案1:全局客户端守卫(在根组件中实现)

在Next.js的根组件中,监听路由变化并校验登录状态:

// pages/_app.tsx 或 app/layout.tsx(App Router)
import { useEffect } from 'react';
import { useRouter } from 'next/router';
import { getUserFromLocalStorage } from '../lib/utils';

export default function MyApp({ Component, pageProps }) {
    const router = useRouter();

    useEffect(() => {
        const checkAuth = () => {
            const user = getUserFromLocalStorage();
            const isLoginPage = router.pathname === '/login';
            
            if (!user && !isLoginPage) {
                router.push('/login');
            }
            if (user && isLoginPage) {
                router.push('/');
            }
        };

        // 初始加载和路由变化时校验
        checkAuth();
        const unsubscribe = router.events.on('routeChangeComplete', checkAuth);

        return () => unsubscribe();
    }, [router]);

    return <Component {...pageProps} />;
}

注意:这种方式会有短暂的未授权页面闪屏,因为是客户端渲染完成后才执行跳转。

方案2:页面级客户端守卫(单个页面)

对需要授权的页面单独添加校验:

// pages/dashboard.tsx
import { useEffect } from 'react';
import { useRouter } from 'next/router';
import { getUserFromLocalStorage } from '../lib/utils';

export default function Dashboard() {
    const router = useRouter();

    useEffect(() => {
        const user = getUserFromLocalStorage();
        if (!user) {
            router.push('/login');
        }
    }, [router]);

    return <div>仪表盘内容</div>;
}

方案3:IndexedDB进阶缓存(适合大量用户数据)

如果需要缓存更多用户数据,可使用IndexedDB,搭配localForage等库简化操作,逻辑和LocalStorage一致:

// utils/indexedDB.ts
import localForage from 'localforage';

export const getUserFromIndexedDB = async (): Promise<User | null> => {
    try {
        const user = await localForage.getItem('user');
        return user as User | null;
    } catch (err) {
        console.error('读取IndexedDB失败:', err);
        return null;
    }
};

然后在客户端守卫中调用这个异步函数做校验。

最佳实践建议

  • 服务端Middleware负责强制路由拦截(用Cookie),避免未授权用户直接访问页面内容
  • 客户端LocalStorage/IndexedDB负责缓存用户信息,减少服务端请求,提升体验
  • 敏感操作(如API请求)仍需在服务端校验用户token的有效性,不能仅依赖客户端缓存

内容的提问来源于stack exchange,提问作者Eduardo Fellipe

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.15 06:45:20