Angular HttpClient中带withCredentials:true和body请求为何触发CORS错误?
问题核心
带请求体的POST请求会触发浏览器的OPTIONS预检请求,当前Cloud Function的CORS配置存在两个关键问题,导致预检不通过:
Access-Control-Allow-Methods仅允许GET方法,但实际请求是POST,预检请求会验证是否允许该方法;- 当请求Origin不在允许列表且非模拟器环境时,
Access-Control-Allow-Origin未被设置,此时即使设置了Access-Control-Allow-Credentials: true,浏览器也会判定该头无效(带凭证的CORS请求要求Access-Control-Allow-Origin必须是具体的Origin,不能为空或通配符)。
修正后的Cloud Function代码
/* eslint-disable max-len */ import {onRequest} from "firebase-functions/v2/https"; import {CookieOptions, Request, Response} from "express"; export const setCookie = onRequest((req, res) => { handleCors(req, res); // 预检OPTIONS请求已在handleCors处理,直接结束 if (req.method === "OPTIONS") { return; } const sessionCookie = (Math.random()* 100000000000000000).toString(); const expiresIn = 60 * 60 * 24 * 5 * 1000; const options: CookieOptions = { maxAge: expiresIn, httpOnly: true, // 仅生产环境启用secure,本地模拟器http环境下关闭,否则浏览器不存储Cookie secure: process.env.NODE_ENV === 'production', sameSite: "none", }; res.cookie("_rememberme", sessionCookie, options); res.send({data: "Set Cookie"}); }); const handleCors = (req: Request, res: Response) => { const origin = req.get("origin") ?? ""; let allowedOrigin = ""; const allowedOrigins = ["https://account.topoint.org", "https://experiences.topoint.org", "https://checkout.topoint.org", "http://localhost:4200"]; // 本地模拟器环境直接允许请求Origin if (process.env.FUNCTIONS_EMULATOR) { allowedOrigin = origin; } else if (allowedOrigins.includes(origin)) { allowedOrigin = origin; } // 仅合法Origin才设置CORS头 if (allowedOrigin) { res.set("Access-Control-Allow-Origin", allowedOrigin); res.set("Access-Control-Allow-Credentials", "true"); } if (req.method === "OPTIONS") { // 允许实际请求使用的POST方法,保留GET res.set("Access-Control-Allow-Methods", "GET, POST"); res.set("Access-Control-Allow-Headers", "Authorization, Content-Type"); res.set("Access-Control-Max-Age", "3600"); res.status(204).send(""); } };
关键修改说明
- 匹配请求方法:将
Access-Control-Allow-Methods改为GET, POST,覆盖实际使用的POST请求; - 优化Origin校验:统一处理模拟器和生产环境的Origin,确保只有合法来源才会设置CORS头,避免空值导致凭证头失效;
- 适配本地环境:生产环境才启用
secure: true,本地HTTP环境下关闭,否则浏览器会拒绝存储Secure Cookie; - 提前终止预检请求:在主函数中判断OPTIONS请求,避免执行后续Cookie设置逻辑。
Angular代码无需修改,保持withCredentials: true配置即可。
内容的提问来源于stack exchange,提问作者Jason Rich Darmawan
相关产品推荐
相关产品推荐

