在构建现代 Web 应用时,身份验证(Authentication) 是几乎每个项目都需要处理的重要功能。当用户登录成功后,服务器通常会返回一个 Token(如 JWT),客户端需要将它保存下来,并在后续请求中用于验证身份。
常见的两种存储方式是:
-
localStorage
:浏览器本地持久化存储,开发者最常用的方式之一 -
Cookie
:浏览器内置的请求头机制,可配置多个安全策略
我在最开始使用 Next.js 构建项目时,和很多人一样优先选择了 localStorage
。但随着对 Next.js(尤其是 App Router 架构)理解的加深,我逐渐发现:localStorage 根本不适合存 Token,最终我全面转向了 使用 Cookie 来存储身份凭证。
在接下来的内容当中,我们将从原理、安全、框架兼容性等角度全面剖析我的这个选择。
Next.js 项目的运行环境:不仅仅是浏览器
Next.js 是一个混合型应用框架,它不仅支持传统的客户端渲染(CSR),还广泛使用:
-
服务端组件(Server Components)
-
中间件(middleware.ts)
-
Edge Function(边缘运行环境)
-
API Routes / Server Actions
-
静态生成(SSG)、服务端渲染(SSR)
这意味着你的项目运行环境不仅包括浏览器(客户端),还包含 Node.js、Edge Runtime 等 无浏览器 API 的环境。
为什么 localStorage 不适合 Next.js 项目?
虽然 localStorage
在前端框架中常被用于临时存储 token,但在 Next.js 项目中,它存在以下硬伤:
1. 无法在服务端访问
Next.js 的服务端组件(Server Component)、Server Action、Edge Function 中无法访问浏览器的 localStorage
,因为它们运行在服务端或边缘节点,而 localStorage
是浏览器环境独有的 API。
tsx
// app/profile/page.tsx - Server Component 中
export default function ProfilePage() {
const token = localStorage.getItem("token"); // ❌ 报错:localStorage is not defined
}
2. 中间件中无法读取 localStorage
middleware.ts
是基于 Edge Runtime 执行的,也无法访问任何浏览器 API,包括 localStorage
。如果你想基于 token 做权限路由控制,就完全不能依赖 localStorage。
ts
// middleware.ts
export function middleware(request: NextRequest) {
const token = localStorage.getItem("token"); // ❌ Edge 环境下不可能存在
}
3. 暴露在客户端,容易被 XSS 攻击
localStorage
是开放的,所有脚本都可以访问。一旦页面存在 XSS 漏洞,token 会直接被窃取,造成严重安全风险。
为什么我选择使用 Cookie?
相较于 localStorage,Cookie 是浏览器原生支持的机制,它在 Next.js 中具备更强的兼容性和安全性:
1. Cookie 可被服务端读取(支持 SSR / Server Components)
Next.js 提供了 cookies()
API,可在 服务端组件、Server Action、API Route、Middleware 等多个环境中访问 Cookie。
tsx
// app/dashboard/page.tsx
import { cookies } from "next/headers";
export default function DashboardPage() {
const token = cookies().get("token")?.value;
if (!token) {
// 重定向或提示登录
}
return <div>欢迎回来!</div>;
}
2. Cookie 会自动随着请求发送,无需额外设置
浏览器会自动把 Cookie 附带到每次同源请求中:
- 页面加载时自动发送给 SSR 服务
- 请求
/api/xxx
时自动带上 Cookie - 服务端可以直接读取,不依赖前端注入
这让身份验证逻辑天然融合到请求流程中,不需要你手动设置任何请求头。
3. Cookie 可配置 HttpOnly,防止 XSS 窃取
设置 HttpOnly
后,JavaScript 无法访问 Cookie,从而彻底解决 XSS 窃取 Token 的风险。
ts
res.cookies.set("token", token, {
httpOnly: true,
secure: true,
maxAge: 3600,
path: "/",
});
4. Cookie 可用于 middleware 权限控制
中间件是页面加载前的"守门员",只支持 Cookie:
ts
// middleware.ts
import { NextRequest, NextResponse } from "next/server";
export function middleware(request: NextRequest) {
const token = request.cookies.get("token")?.value;
if (!token) {
return NextResponse.redirect(new URL("/login", request.url));
}
return NextResponse.next();
}
你可以精准拦截未登录用户、限制特定路由访问权限。
举个例子:登录/登出流程(App Router)
接下来我们将使用一个登录/登出的例子来讲解一下 Cookie 在 NextJs项目中的应用。
登录时设置 Cookie
ts
// app/api/login/route.ts
import { NextResponse } from "next/server";
export async function POST(request: Request) {
const body = await request.json();
const token = await verifyUserAndGenerateToken(body);
const response = NextResponse.json({ success: true });
response.cookies.set("token", token, {
httpOnly: true,
path: "/",
maxAge: 60 * 60 * 24,
});
return response;
}
服务端组件中读取 token
tsx
// app/dashboard/page.tsx
import { cookies } from "next/headers";
import { redirect } from "next/navigation";
export default function DashboardPage() {
const token = cookies().get("token")?.value;
if (!token) {
redirect("/login");
}
return <div>用户中心</div>;
}
middleware 权限拦截
ts
// middleware.ts
import { NextRequest, NextResponse } from "next/server";
export function middleware(request: NextRequest) {
const token = request.cookies.get("token")?.value;
const isProtected = request.nextUrl.pathname.startsWith("/dashboard");
if (isProtected && !token) {
return NextResponse.redirect(new URL("/login", request.url));
}
return NextResponse.next();
}
登出时清除 Cookie
ts
// app/api/logout/route.ts
import { NextResponse } from "next/server";
export async function POST() {
const response = NextResponse.json({ success: true });
response.cookies.set("token", "", {
maxAge: 0,
path: "/",
});
return response;
}
总结
在 App Router 架构下,Next.js 已不再是一个纯前端框架,而是一个真正的全栈应用平台。因此,Token 的存储方式也必须适应全栈环境的特性。
Cookie,作为浏览器与服务端之间天然的桥梁,是目前在 Next.js 项目中存储 Token 最合理、最安全、最兼容的方式。
如果你还在用 localStorage
存 token,是时候升级你的身份认证方案了。
功能/特性 | Cookie ✅ | localStorage ❌ |
---|---|---|
SSR / RSC 可访问 | ✅ cookies() 可用 |
❌ 无法在服务端读取 |
middleware 可访问 | ✅ request.cookies 可用 |
❌ 不存在 localStorage |
自动附加请求 | ✅ 默认随请求发送 | ❌ 需手动加到 headers |
可设置 HttpOnly | ✅ 防止 XSS | ❌ 直接暴露给 JS |
安全性 | ✅ 高,可配置 Secure/SameSite | ❌ 容易被 XSS 攻击 |
易用性(在 App Router) | ✅ 与新架构天然兼容 | ❌ 基本不可用 |