跨域部署(Netlify+Heroku)下React+Node.js的Cookie存储问题
问题描述
将React前端部署在Netlify、Node.js后端部署在Heroku后,Cookie设置功能失效。此前两者同部署在Heroku时功能正常,但该方案无法支持前端从GitHub推送代码时自动部署。当前后端在Heroku、前端在Netlify,登录请求中服务器已正常发送Cookie,但浏览器未将其存入Cookie存储。
相关代码
后端index.js
const express = require("express"); const app = express(); const cookieParser = require("cookie-parser"); const authRoute = require("./routes/auth-route.js"); app.set("trust proxy",1); // 跨域请求中间件 app.use( cors({ credentials: true, origin: "https://myfrontend.com", }) ); // 解析请求中的Cookie app.use(cookieParser({})); // 解析请求体 app.use(express.json()); // 路由转发 app.use("/api/auth", authRoute);
后端authRoute.js
const router = require("express").Router(); const {login} = require("../controllers/auth-controller"); router.post("/login", login); module.exports = router;
后端auth-controller.js
const bcrypt = require("bcrypt"); const User = require("../models/User"); const generateJwt = require("../utils/generateJWT"); const login = async (req, res) => { try { // 根据邮箱查找用户 const user = await User.findOne({ email: req.body.email }); if (!user) return res.status(404).json({ success: false, message: "Email or Password Incorrect" }); // 验证密码 const validatePassword = await bcrypt.compare(req.body.password, user.password); if (!validatePassword) return res.status(404).json({ success: false, message: "Email or Password Incorrect" }); // 剔除密码字段 const { password, ...others } = user._doc; // 生成JWT const token = generateJwt(user._id); // 设置Cookie // 勾选rememberMe则Cookie有效期30天,否则为会话期 if (req.body.rememberMe === true) { res.cookie("isLogged", true, { sameSite: "none", secure: true, domain: "app-frontend.netlify.app", maxAge: 2592000000, // 30天 }); res.cookie("authToken", token, { httpOnly: true, sameSite: "none", domain: "app-frontend.netlify.app", secure: true, maxAge: 2592000000, // 30天 }); } else { res.cookie("isLogged", true, { sameSite: "none", domain: "app-frontend.netlify.app", secure: true, }); res.cookie("authToken", token, { httpOnly: true, sameSite: "none", domain: "app-frontend.netlify.app", secure: true, }); } res.status(200).json({ success: true, user: others }); } catch (err) { console.log(err); res.status(500).json({ success: false, message: err }); } }; module.exports = {login };
前端axiosConfig.js
import axios from "axios"; const axiosInstance = axios.create({ withCredentials: true, baseURL: "https://app-backend.herokuapp.com/api/", }); export default axiosInstance;
前端登录函数
import axios from "../../utils/axiosConfig"; const login = async () => { try { dispatch(setIsLoading(true)); const data = { email: email.current.value, password: password.current.value, rememberMe: rememberMe.current.checked, }; const res = await axios.post("auth/login", data); dispatch(setIsLoading(false)); if (res.status === 200) { dispatch(setUser(res.data.user)); } } catch (err) { dispatch( setIsError({ status: true, message: err.response.data.message, }) ); dispatch(setIsLoading(false)); } };
解决方案
1. 移除Cookie的domain配置
跨域场景下,浏览器会拒绝存储来自不同域名服务器设置的、指定为其他域名的Cookie。直接删除所有Cookie配置中的domain字段,让浏览器自动绑定Cookie到后端的Heroku域名:
修改auth-controller.js中的Cookie设置:
// 勾选rememberMe时的配置 res.cookie("isLogged", true, { sameSite: "none", secure: true, maxAge: 2592000000, }); res.cookie("authToken", token, { httpOnly: true, sameSite: "none", secure: true, maxAge: 2592000000, }); // 未勾选rememberMe时的配置 res.cookie("isLogged", true, { sameSite: "none", secure: true, }); res.cookie("authToken", token, { httpOnly: true, sameSite: "none", secure: true, });
2. 修正CORS的origin配置
确保index.js中CORS的origin与Netlify前端的实际域名完全一致,比如你的前端域名是https://app-frontend.netlify.app,则修改为:
app.use( cors({ credentials: true, origin: "https://app-frontend.netlify.app", }) );
3. 确认代理信任设置
当前的app.set("trust proxy", 1)配置正确,Heroku使用反向代理,该设置能让Express正确识别请求的协议和客户端信息,保证secure Cookie正常生效。
4. 前端配置无需修改
你的Axios实例已经开启withCredentials: true,该配置会让前端请求自动携带Cookie,无需调整。
原理说明
跨域场景中,浏览器的同源策略禁止服务器为其他域名设置Cookie。删除domain字段后,Cookie会被绑定到后端的Heroku域名,当前端发送请求时,只要withCredentials开启,浏览器就会自动将该Cookie携带到后端,实现身份验证。
内容的提问来源于stack exchange,提问作者tedGuy
相关产品推荐
相关产品推荐

