HttpOnly Cookie已随响应发送但浏览器未存储(NestJS+NextJS Apollo)
问题现象
HttpOnly Cookie已通过后端NestJS服务的响应发送,但在浏览器开发者工具的Application>Cookies面板中无法找到该Cookie。
后端NestJS登录接口代码
async loginSeller(loginSellerInput: LoginSellerInput, ctx: any) { const user = await this.authService.validateUser( loginSellerInput.email, loginSellerInput.password, ); if (!user) { throw new BadRequestException(`Email or password are invalid`); } else { const seller = await this.sellerModel.findOne({ email: loginSellerInput.email, }); seller.last_connected = new Date(); seller.save(); const tokens = await this.authService.generateUserCredentials(user); const serialised = serialize('OursiteJWT', tokens.access_token, { httpOnly: true, secure: process.env.NODE_ENV !== 'development', sameSite: 'strict', maxAge: 60 * 60 * 24 * 30, path: '/', }); await this.usersService.updateRtHash(user.id, tokens.refresh_token); return tokens; } }
前端NextJS Apollo客户端配置
import { ApolloClient, createHttpLink, InMemoryCache } from "@apollo/client"; import { setContext } from "@apollo/client/link/context"; const httpLink = createHttpLink({ uri: "http://localhost:3000/graphql", credentials: "same-origin", }); const authLink = setContext((_, { headers }) => { // get the authentication token from local storage if it exists // const token = localStorage.getItem("token"); // return the headers to the context so httpLink can read them return { headers: { ...headers, // authorization: token ? `Bearer ${token}` : "", }, }; }); const client = new ApolloClient({ link: authLink.concat(httpLink), cache: new InMemoryCache(), }); // const client = new ApolloClient({ // uri: "http://localhost:3000/graphql", // cache: new InMemoryCache(), // }); export default client;
已确认信息
- Cookie确实已通过后端响应发送(可通过网络请求的响应头验证)
- 浏览器端
Application>Cookies中无该Cookie显示
已尝试方案
- 在NestJS的
main.ts中启用CORS配置 - 修改Apollo客户端的相关配置,但问题未解决
问题排查与解决方案
1. 核心问题:未将序列化Cookie添加到响应头
当前代码仅生成了Cookie的序列化字符串,但没有将其注入到响应头中。在NestJS中,需要通过ctx对象获取响应实例,手动设置Set-Cookie头:
// 在return tokens之前添加以下代码 ctx.res.setHeader('Set-Cookie', serialised);
这是浏览器接收不到Cookie的最直接原因。
2. 修正CORS配置
确保NestJS的CORS配置允许携带凭证,且Origin与前端地址完全匹配。在main.ts中配置:
async function bootstrap() { const app = await NestFactory.create(AppModule); app.enableCors({ origin: 'http://localhost:3001', // 替换为你的前端实际运行地址 credentials: true, // 必须设为true,允许跨域携带Cookie }); await app.listen(3000); } bootstrap();
3. 调整Apollo客户端的凭证配置
如果前后端运行在不同端口(如后端3000、前端3001),same-origin会导致凭证无法正常传递,需改为include:
const httpLink = createHttpLink({ uri: "http://localhost:3000/graphql", credentials: "include", // 跨域场景下使用include });
4. 验证开发环境下的secure属性
开发环境通常使用HTTP协议,此时secure: true会被浏览器拒绝存储。当前代码中secure: process.env.NODE_ENV !== 'development'的逻辑是正确的,但需确保NODE_ENV环境变量确实被设置为development。
5. 补充Cookie的Domain属性(跨端口场景)
若前后端同域名但不同端口,可指定domain为localhost(不要加端口),确保Cookie能被共享:
const serialised = serialize('OursiteJWT', tokens.access_token, { httpOnly: true, secure: process.env.NODE_ENV !== 'development', sameSite: 'lax', // 严格模式下跨域可能受限,可先改为lax测试 maxAge: 60 * 60 * 24 * 30, path: '/', domain: 'localhost', // 添加该属性 });
内容的提问来源于stack exchange,提问作者Aymane Elmadidi
相关产品推荐
相关产品推荐

