Next.js 13.4如何利用Redux Toolkit存储的Token实现路由保护?
解决方案:Next.js 中结合 Token 实现路由保护(无法读取 Redux 数据的处理)
首先明确核心问题:Next.js 的 middleware.js 运行在边缘/服务器端,而 Redux 状态是存储在客户端浏览器内存中的,两者运行环境完全隔离,因此无法直接在 Middleware 中读取 Redux 数据。下面是几种可行的解决思路:
1. 首选方案:将 Token 存入 Cookie,在 Middleware 中读取验证
这是最符合 Next.js 架构且安全的方式,因为 Middleware 可以直接读取请求 Cookie。
步骤1:登录成功后将 Token 写入 Cookie
登录接口返回 Token 后,除了存入 Redux,同时将 Token 写入 HttpOnly、Secure 类型的 Cookie(生产环境下开启 Secure,防止非 HTTPS 传输):
// 客户端登录逻辑(比如登录页面的提交函数) import { useDispatch } from 'react-redux'; import { setToken } from '@/redux/slices/authSlice'; async function handleLogin(formData) { const res = await fetch('/api/login', { method: 'POST', body: formData }); const { token } = await res.json(); // 存入 Redux const dispatch = useDispatch(); dispatch(setToken(token)); // 写入 Cookie document.cookie = `authToken=${token}; path=/; secure=${process.env.NODE_ENV === 'production'}; httpOnly=true; SameSite=Strict`; }
注:HttpOnly Cookie 无法被前端 JS 读取,能有效防止 XSS 攻击,适合存储认证 Token。
步骤2:在 Middleware 中拦截并验证 Token
// middleware.js import { NextResponse } from 'next/server'; import type { NextRequest } from 'next/server'; export function middleware(request: NextRequest) { // 读取 Cookie 中的 Token const token = request.cookies.get('authToken')?.value; // 定义需要保护的路由 const protectedRoutes = ['/dashboard', '/profile', '/orders']; // 检查当前请求是否访问受保护路由,且无有效 Token const isAccessingProtectedRoute = protectedRoutes.some(route => request.nextUrl.pathname.startsWith(route) ); if (isAccessingProtectedRoute && !token) { // 重定向到登录页,同时记录原路径方便登录后跳转 const loginUrl = new URL('/login', request.url); loginUrl.searchParams.set('redirect', request.nextUrl.pathname); return NextResponse.redirect(loginUrl); } // 有有效 Token,允许继续访问 return NextResponse.next(); } // 指定 Middleware 生效的路由范围 export const config = { matcher: ['/dashboard/:path*', '/profile/:path*', '/orders/:path*'], };
2. 客户端路由保护(作为补充方案)
如果场景限制无法使用 Cookie,可在客户端组件中结合 Redux 做路由拦截,但注意这种方式只能做前端层面的保护,无法阻止用户直接访问页面(页面会先加载再跳转),建议配合服务端验证。
实现客户端路由保护组件
'use client'; import { useEffect } from 'react'; import { useSelector } from 'react-redux'; import { usePathname, useRouter } from 'next/navigation'; export default function ProtectedRoute({ children }) { const token = useSelector(state => state.auth.token); const pathname = usePathname(); const router = useRouter(); const protectedPaths = ['/dashboard', '/profile', '/orders']; useEffect(() => { const needsProtection = protectedPaths.some(path => pathname.startsWith(path)); if (needsProtection && !token) { router.push(`/login?redirect=${pathname}`); } }, [token, pathname, router]); // 未登录时可返回加载态或空 if (!token) return <div>Loading...</div>; return children; }
在受保护页面中使用该组件
// app/dashboard/page.js import ProtectedRoute from '@/components/ProtectedRoute'; export default function DashboardPage() { return ( <ProtectedRoute> <div>你的仪表盘内容</div> </ProtectedRoute> ); }
3. Server Component 服务端验证(App Router)
对于 App Router 的 Server Component,可以直接读取 Cookie 并验证 Token 有效性,无效则重定向:
// app/orders/page.js import { cookies } from 'next/headers'; import { redirect } from 'next/navigation'; export default async function OrdersPage() { const token = cookies().get('authToken')?.value; // 调用后端接口验证 Token 有效性 const isValidToken = await fetch(`${process.env.NEXT_PUBLIC_API_URL}/verify-token`, { headers: { Authorization: `Bearer ${token}` } }).then(res => res.ok); if (!token || !isValidToken) { redirect('/login'); } // 验证通过,渲染页面内容 return <div>你的订单列表</div>; }
内容的提问来源于stack exchange,提问作者Prem sagar Vishwakarma
相关产品推荐
相关产品推荐

