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

Nest后端Session认证与Next.js前端集成:Cookie无法保存问题求助

问题

已实现基于Session的Nest后端认证,使用Insomnia发起请求时可正常保存Session Cookie,但在Next.js前端发起登录请求时,Cookie无法在前端存储。不过登录后Redis数据库中已生成对应的Session ID。查阅NextAuth相关资料,未找到适配该场景的解决方案。

关键现象:

  • 使用Insomnia发起请求可正常保存Session Cookie;
  • 前端登录后,Redis数据库中已存在Session ID,但前端无Cookie存储。

相关代码如下:

前端AuthContext代码

import { createContext } from 'react';

type AuthContextType = {
  isAuthenticated: boolean;
  signIn: ({ email, password }: { email: string, password: string }) => Promise<void>;
};

export const AuthContext = createContext({} as AuthContextType);

export function AuthProvider({ children }: { children: React.ReactNode }) {
  const isAuthenticated = false;

  async function signIn({ email, password }: { email: string, password: string }): Promise<void> {
    //fazer request para minha api backend que vai retornar um session cookie
    const response = await fetch('http://localhost:3000/auth/login', {
      method: 'POST',
      body: JSON.stringify({ email, password }),
      headers: {
        'Content-Type': 'application/json'
      }
    })
     
    console.log(response.json());
  }

  return (
    <AuthContext.Provider value={{ isAuthenticated, signIn }}>
      {children}
    </AuthContext.Provider>
  );
}

前端登录页面代码

import { useState, useContext } from 'react';
import { useRouter } from 'next/router';
import { AuthContext } from '../contexts/AuthContext';

function LoginPage() {
  const [email, setEmail] = useState('');
  const [password, setPassword] = useState('');
  const { signIn } = useContext(AuthContext);
  const Router = useRouter();

  const handleLogin = async (event: React.FormEvent) => {
    event.preventDefault();

    const data = {
      email: email,
      password: password
    };

    try {
      await signIn(data);
      Router.push('/dashboard');
    } catch (err) {
      console.log(err);
    }
  };

  return (
    <form onSubmit={handleLogin}>
      <input
        type="email"
        value={email}
        onChange={(e) => setEmail(e.target.value)}
        placeholder="Email"
        required
      />
      <input
        type="password"
        value={password}
        onChange={(e) => setPassword(e.target.value)}
        placeholder="Password"
        required
      />
      <button type="submit">Login</button>
    </form>
  );
}

export default LoginPage;

后端app.js代码

async function bootstrap() {
  const app = await NestFactory.create(AppModule, { cors: true });
  const PORT = Number(process.env.SERVER_PORT) || 3000;
  const client = redis.createClient({ url: process.env.REDIS_URL });

  const RedisStore = connectRedis(session);

  app.use(
    session({
      name: 'session',
      secret: process.env.SESSION_SECRET,
      resave: false,
      saveUninitialized: false,
      store: new RedisStore({
        client,
        ttl: 86400,
      }),
      cookie: {
        maxAge: 24 * 60 * 60 * 1000,
      },
    }),
  );

  app.use(passport.initialize());
  app.use(passport.session());
  await app.listen(PORT, () => console.log(`Server started on port ${PORT}`));
}
问题根源
  • 前端Fetch请求默认不携带/保存Cookie,需显式开启凭证配置;
  • 后端仅设置cors: true会导致Origin为*,与凭证传递规则冲突,无法正常下发Cookie;
  • Session Cookie的sameSite、secure参数未适配跨域场景,导致浏览器拒绝存储。
解决方案

1. 前端修改:配置Fetch凭证选项

在AuthContext的signIn函数中添加credentials: 'include',允许跨域请求携带并保存Cookie:

async function signIn({ email, password }: { email: string, password: string }): Promise<void> {
  const response = await fetch('http://localhost:3000/auth/login', {
    method: 'POST',
    body: JSON.stringify({ email, password }),
    headers: {
      'Content-Type': 'application/json'
    },
    // 关键:开启凭证携带,允许跨域保存Cookie
    credentials: 'include'
  });

  if (!response.ok) {
    throw new Error('登录失败');
  }

  const result = await response.json();
  console.log(result);
}

2. 后端修改:完善CORS与Session配置

2.1 配置精准CORS规则

替换原有的cors: true,指定前端Origin并允许凭证传递:

async function bootstrap() {
  const app = await NestFactory.create(AppModule);
  const PORT = Number(process.env.SERVER_PORT) || 3000;
  const client = redis.createClient({ url: process.env.REDIS_URL });

  // 配置CORS,替换为你的Next.js前端地址
  app.enableCors({
    origin: 'http://localhost:3001',
    credentials: true,
    allowedHeaders: ['Content-Type', 'Authorization'],
    methods: ['GET', 'POST', 'PUT', 'DELETE', 'OPTIONS']
  });

  const RedisStore = connectRedis(session);

  app.use(
    session({
      name: 'session',
      secret: process.env.SESSION_SECRET,
      resave: false,
      saveUninitialized: false,
      store: new RedisStore({
        client,
        ttl: 86400,
      }),
      cookie: {
        maxAge: 24 * 60 * 60 * 1000,
        httpOnly: true, // 防止XSS攻击,推荐开启
        sameSite: process.env.NODE_ENV === 'production' ? 'none' : 'lax',
        secure: process.env.NODE_ENV === 'production', // 生产环境需开启HTTPS
      },
    }),
  );

  app.use(passport.initialize());
  app.use(passport.session());
  await app.listen(PORT, () => console.log(`Server started on port ${PORT}`));
}

参数说明

  • origin:必须指定前端具体地址,不能用*(否则credentials: true会失效);
  • sameSite:开发环境用lax,生产跨域部署时设为none;
  • secure:生产环境开启HTTPS时设为true,确保Cookie仅在HTTPS下传递。

3. 验证步骤

  1. 重启Nest后端与Next.js前端;
  2. 发起登录请求,查看浏览器控制台Application -> Cookies,确认session Cookie已存在;
  3. 后续请求需保持credentials: 'include'配置,确保携带Session Cookie。
补充:Next.js代理方案(可选)

若不想处理跨域,可在next.config.js中配置反向代理:

module.exports = {
  async rewrites() {
    return [
      {
        source: '/api/auth/:path*',
        destination: 'http://localhost:3000/auth/:path*',
      },
    ];
  },
};

此时前端请求改为/api/auth/login,Fetch的credentials可改为'same-origin'。

内容的提问来源于stack exchange,提问作者Edson Magno

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.01 14:48:11