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

基于Next.js与Nest.js的服务端Cookie设置问题排查

问题分析与解决方案

核心问题

  1. 跨域CORS配置缺失:Nest.js服务端未配置允许跨域携带凭证,导致浏览器拒绝保存服务端返回的HttpOnly Cookie。
  2. Server Action的误区:登录请求用了Next.js Server Action("use server"),代码运行在服务器端而非浏览器端,此时fetch的credentials: "include"只会让服务器端请求携带Cookie,浏览器无法接收服务端的Set-Cookie响应头。
  3. 手动设置Cookie的错误操作:你从Set-Cookie头提取Token并手动设置非HttpOnly Cookie,这不仅破坏了HttpOnly的安全特性,还直接说明浏览器根本没正确接收服务端返回的HttpOnly Cookie。

解决方案步骤

1. 修复Nest.js服务端CORS配置

在main.ts中添加CORS配置,明确允许客户端Origin并开启凭证支持:

// src/main.ts
async function bootstrap() {
  const app = await NestFactory.create(AppModule);
  
  // 配置CORS,替换成你的Next.js客户端地址
  app.enableCors({
    origin: 'http://localhost:3000', // 开发环境地址,生产环境换成实际域名
    credentials: true, // 必须开启,允许跨域携带Cookie
    allowedHeaders: ['Content-Type', 'Authorization'],
    methods: ['GET', 'POST', 'PUT', 'DELETE'],
  });

  await app.listen(8080);
}
bootstrap();

同时调整服务端Cookie配置,根据环境动态设置secure:

// 登录接口中的Cookie配置
response.cookie('jwt', access_token, {
  sameSite: process.env.NODE_ENV === 'production' ? 'none' : 'lax',
  path: '/',
  secure: process.env.NODE_ENV === 'production', // 生产环境必须为true(HTTPS)
  httpOnly: true,
  maxAge: 3600000, // 可选:设置Cookie有效期,比如1小时
});

2. 调整Next.js客户端登录逻辑

将登录请求从Server Action改为客户端组件逻辑,确保请求在浏览器端发起,让浏览器能正确接收并存储HttpOnly Cookie:

// app/login/page.tsx
'use client'; // 标记为客户端组件

import { redirect } from 'next/navigation';
import { useState } from 'react';

export default function LoginPage() {
  const [error, setError] = useState('');

  async function handleSubmit(e: React.FormEvent<HTMLFormElement>) {
    e.preventDefault();
    const formData = new FormData(e.currentTarget);
    const username = formData.get('username') as string;
    const password = formData.get('password') as string;

    try {
      const response = await fetch('http://localhost:8080/auth/signIn', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        credentials: 'include', // 浏览器端请求必须带这个,让浏览器自动处理Cookie
        body: JSON.stringify({ username, password }),
      });

      if (!response.ok) {
        setError('用户名或密码错误');
        return;
      }

      // 登录成功后跳转首页,此时浏览器已自动存储HttpOnly Cookie
      redirect('/');
    } catch (err) {
      setError('登录失败,请重试');
    }
  }

  return (
    <form onSubmit={handleSubmit}>
      {error && <p style={{ color: 'red' }}>{error}</p>}
      <input type="text" name="username" placeholder="用户名" required />
      <input type="password" name="password" placeholder="密码" required />
      <button type="submit">登录</button>
    </form>
  );
}

删除以下错误代码:

// 这段代码完全不需要,会创建非HttpOnly Cookie,破坏安全特性
const setCookieHeader = response.headers.get("Set-Cookie");
if (setCookieHeader) {
  const jwtToken = setCookieHeader.split(";")[0].split("=")[1];
  console.log("JWT Token:", jwtToken);
  cookies().set({ name: "jwt", value: jwtToken, secure: true, path: "/" });
}

3. 验证HttpOnly Cookie

登录成功后,打开浏览器开发者工具:

  • 进入Application标签 -> Cookies -> 查看服务端地址(localhost:8080),应该能看到jwt Cookie,且HttpOnly选项被勾选。
  • 此时在控制台执行document.cookie,不会看到jwt Cookie(这是HttpOnly的正常安全表现)。

4. 确保Next.js中间件能读取Cookie

如果首页需要认证,在middleware.ts中可以通过request.cookies.get('jwt')获取Cookie(中间件运行在边缘节点,能拿到请求头里的Cookie):

// middleware.ts
import { NextResponse } from 'next/server';
import type { NextRequest } from 'next/server';

export function middleware(request: NextRequest) {
  const jwt = request.cookies.get('jwt');
  
  // 如果没有Cookie,重定向到登录页
  if (!jwt) {
    return NextResponse.redirect(new URL('/login', request.url));
  }
  
  // 可选:调用Nest.js接口验证Token有效性
  // ...
  
  return NextResponse.next();
}

// 配置需要认证的路由
export const config = {
  matcher: ['/', '/dashboard/:path*'],
};

关键注意事项

  • 跨域场景下,sameSite和secure必须配合正确:生产环境用sameSite: 'none'时,secure必须为true(仅HTTPS);开发环境用sameSite: 'lax'即可。
  • Server Action适合处理服务器端逻辑,但登录请求涉及浏览器Cookie存储,必须在客户端发起。
  • HttpOnly Cookie的核心作用是防止XSS攻击,绝对不要手动提取并设置为非HttpOnly Cookie。

内容的提问来源于stack exchange,提问作者Max Taylor-Hayden

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.28 00:47:31