Next.js 14与Express.js间HttpOnly Cookie的设置、传递及页面保护
一、核心前提:HttpOnly Cookie的特性
HttpOnly Cookie不允许前端JS读取或修改,这是安全设计。浏览器会自动负责Cookie的存储、携带,你不需要在前端手动设置它,只需要确保请求配置正确,让浏览器能正常接收和发送Cookie。
二、第一步:后端完善CORS配置(关键!)
你当前的代码缺少CORS配置,这是前端无法接收Cookie的常见原因。在Express的server.js中添加CORS中间件:
const cors = require('cors'); // 配置CORS,允许前端域名携带凭证 app.use(cors({ origin: 'http://localhost:3000', // 替换为你的Next.js前端地址,生产环境用实际域名 credentials: true, // 必须开启,允许请求携带Cookie }));
三、Next.js中接收Cookie(登录流程)
前端发起登录请求时,必须携带credentials: 'include',让浏览器保存后端返回的HttpOnly Cookie。以下是登录页面的示例代码:
// app/login/page.js(Client Component) 'use client'; export default function LoginPage() { const handleLogin = async (e) => { e.preventDefault(); const formData = new FormData(e.target); const res = await fetch('http://localhost:5000/api/v1/users/login', { method: 'POST', headers: { 'Content-Type': 'application/x-www-form-urlencoded', // 匹配后端upload.none()的表单格式 }, body: new URLSearchParams({ username: formData.get('username'), password: formData.get('password'), }), credentials: 'include', // 关键:允许浏览器接收并存储Cookie }); if (!res.ok) { const error = await res.json(); alert(error.message); return; } // 登录成功后跳转,浏览器已自动保存Cookie window.location.href = '/protected'; }; return ( <form onSubmit={handleLogin}> <input name="username" placeholder="用户名" required /> <input name="password" type="password" placeholder="密码" required /> <button type="submit">登录</button> </form> ); }
四、Next.js向后端自动携带Cookie
所有需要授权的请求,都要添加credentials: 'include',浏览器会自动把存储的HttpOnly Cookie发送给后端:
// 示例:获取受保护数据(Client Component) async function fetchProtectedData() { const res = await fetch('http://localhost:5000/api/v1/data', { credentials: 'include', // 自动携带Cookie }); if (!res.ok) { // 未授权时跳转登录页 window.location.href = '/login'; return null; } return await res.json(); }
五、Next.js中保护特定页面
推荐使用Middleware实现全局路由保护,也可以在Server Component中单独验证,两种方式任选:
方式1:Middleware全局保护(推荐)
在app/middleware.js中编写中间件,拦截未授权请求:
import { NextResponse } from 'next/server'; export async function middleware(request) { const token = request.cookies.get('token')?.value; // 定义需要保护的路由 const protectedRoutes = ['/protected', '/dashboard']; const isProtected = protectedRoutes.includes(request.nextUrl.pathname); // 未授权访问保护路由,重定向到登录页 if (isProtected && !token) { return NextResponse.redirect(new URL('/login', request.url)); } // 已登录状态访问登录页,重定向到首页 if (request.nextUrl.pathname === '/login' && token) { // 额外验证token有效性,避免过期Cookie残留 const verifyRes = await fetch('http://localhost:5000/api/v1/verify-token', { method: 'POST', body: JSON.stringify({ token }), headers: { 'Content-Type': 'application/json' }, }); if (verifyRes.ok) { return NextResponse.redirect(new URL('/', request.url)); } } return NextResponse.next(); } // 指定中间件作用的路由 export const config = { matcher: ['/login', '/protected/:path*', '/dashboard/:path*'], };
需要在后端添加一个token验证接口(userController.js):
router.post('/verify-token', async (req, res) => { try { const token = req.cookies.token || req.body.token; if (!token) return res.status(401).json({ success: false, message: '无有效凭证' }); // 验证JWT有效性 const decoded = JWT.verify(token, secret); res.status(200).json({ success: true, user: decoded }); } catch (err) { res.status(401).json({ success: false, message: '凭证无效或过期' }); } });
方式2:Server Component页面内验证
在需要保护的页面(如app/protected/page.js)中直接验证:
import { cookies } from 'next/headers'; import { redirect } from 'next/navigation'; export default async function ProtectedPage() { const cookieStore = cookies(); const token = cookieStore.get('token')?.value; if (!token) redirect('/login'); // 验证token有效性 const verifyRes = await fetch('http://localhost:5000/api/v1/verify-token', { method: 'POST', body: JSON.stringify({ token }), headers: { 'Content-Type': 'application/json' }, }); if (!verifyRes.ok) redirect('/login'); const user = await verifyRes.json(); return ( <div> <h1>受保护页面</h1> <p>欢迎,用户ID:{user.user.id}</p> </div> ); }
六、生产环境注意事项
- 开启Cookie的
secure: true:仅在HTTPS环境下传输Cookie,符合安全规范 - 调整
sameSite属性:如果前后端是跨域名,设置sameSite: 'none'并配合secure: true;同域名用lax即可 - CORS的
origin设置为生产环境的实际域名,不要用通配符*(会导致credentials失效)
内容的提问来源于stack exchange,提问作者user10033434
相关产品推荐
相关产品推荐

