如何在NextJS 13中用API路由、中间件实现用户认证及Cookie操作
核心对应关系
你在Express中通过protect中间件验证JWT、从Cookie取token、挂载req.user的逻辑,在Next.js 13中可以通过全局Middleware + API路由处理函数结合实现,同时Next.js对Cookie的读取/写入有专门的API支持。
1. 准备工具函数与依赖
先安装所需依赖:
npm install jsonwebtoken @types/jsonwebtoken cookie # 或使用yarn yarn add jsonwebtoken @types/jsonwebtoken cookie
创建通用JWT处理工具utils/auth.ts:
import jwt from "jsonwebtoken"; import { User } from "@/models/userModel"; // 你的User模型 // 验证JWT并返回用户信息 export const verifyToken = async (token: string): Promise<User | null> => { try { const decoded = jwt.verify(token, process.env.JWT_SECRET!) as { userId: string }; // 从数据库获取用户,排除密码字段 const user = await User.findById(decoded.userId).select("-password"); return user; } catch (err) { return null; } };
2. 实现全局认证中间件(Middleware)
Next.js 13的Middleware可在请求到达API路由/页面之前拦截处理,类似Express的全局中间件。在项目根目录创建middleware.ts:
import { NextResponse } from "next/server"; import type { NextRequest } from "next/server"; import { verifyToken } from "@/utils/auth"; export async function middleware(request: NextRequest) { // 从Cookie中获取JWT token const token = request.cookies.get("jwt")?.value; // 定义需要认证的路由 const protectedPaths = ["/api/posts", "/api/profile"]; const isProtected = protectedPaths.some(path => request.nextUrl.pathname.startsWith(path)); if (isProtected) { if (!token) { // 无token,返回401 return new NextResponse(JSON.stringify({ error: "Not authorized, no token" }), { status: 401, headers: { "Content-Type": "application/json" }, }); } const user = await verifyToken(token); if (!user) { // token无效,返回401 return new NextResponse(JSON.stringify({ error: "Not authorized, invalid token" }), { status: 401, headers: { "Content-Type": "application/json" }, }); } // 将用户ID通过请求头传递给后续路由 const requestHeaders = new Headers(request.headers); requestHeaders.set("x-user-id", user._id.toString()); return NextResponse.next({ request: { headers: requestHeaders }, }); } // 非保护路由直接放行 return NextResponse.next(); } // 指定Middleware生效的路由范围 export const config = { matcher: ["/api/:path*"], };
注意: 若想直接在NextRequest上挂载user属性,可扩展类型:在types/next.d.ts中添加:
import type { User } from "@/models/userModel"; declare module "next/server" { interface NextRequest { user?: User; } }
3. 在API路由中获取当前用户
类似Express中req.user的用法,在Next.js 13的API路由(App Router)中可这样获取用户:
示例:app/api/posts/route.ts
import { NextRequest, NextResponse } from "next/server"; import asyncHandler from "express-async-handler"; import { User } from "@/models/userModel"; // 处理POST请求创建帖子 export const POST = asyncHandler(async (req: NextRequest) => { const { content, image } = await req.json(); // 从请求头获取用户ID并查询用户 const userId = req.headers.get("x-user-id"); if (!userId) { return NextResponse.json({ error: "Not authorized" }, { status: 401 }); } const user = await User.findById(userId).select("-password"); if (!user) { return NextResponse.json({ error: "User not found" }, { status: 404 }); } // 后续逻辑:创建帖子并关联用户 // const post = await Post.create({ content, image, author: user._id }); return NextResponse.json({ message: "Post created", userName: user.name }, { status: 201 }); });
4. Cookie的读写操作
读取Cookie
在Middleware或API路由中,通过request.cookies.get("cookie-name")?.value读取:
const token = request.cookies.get("jwt")?.value;
设置Cookie
在登录接口中设置Cookie,示例:app/api/auth/login/route.ts
import { NextResponse } from "next/server"; import jwt from "jsonwebtoken"; import User from "@/models/userModel"; export const POST = async (req: NextRequest) => { const { email, password } = await req.json(); // 验证用户密码逻辑(省略) const user = await User.findOne({ email }); // ...密码验证步骤 // 生成JWT const token = jwt.sign({ userId: user._id }, process.env.JWT_SECRET!, { expiresIn: "7d" }); // 设置Cookie const response = NextResponse.json({ message: "Login successful", user: { name: user.name, email: user.email } }); response.cookies.set("jwt", token, { httpOnly: true, // 防止XSS攻击 secure: process.env.NODE_ENV === "production", // 生产环境启用HTTPS maxAge: 7 * 24 * 60 * 60, // 7天有效期 path: "/", // 全站生效 }); return response; };
删除Cookie
登出时清除Cookie:
export const POST = async () => { const response = NextResponse.json({ message: "Logged out" }); response.cookies.set("jwt", "", { httpOnly: true, secure: process.env.NODE_ENV === "production", maxAge: 0, // 立即过期 path: "/", }); return response; };
内容的提问来源于stack exchange,提问作者Bipin
相关产品推荐
相关产品推荐

