我在不同的服务器上运行我的项目,后端在渲染上,前端在 vercel 上。然而,我只是在部署时遇到这个错误,这意味着 cookie 要么没有在客户端正确设置,要么没有被正确检索。
我已经检查了 cors 和 axios 中的凭据。我还检查了 res.cookies 中的“withCredentials”以及其他参数是否设置为 true。控制台中没有错误。登录显示成功。它只是不接收 cookie,因此无法重定向到主页。我不知道还能做什么。
这是express服务器上的登录功能
const Login = async (req, res, next) => {
try {
const { email, password } = req.body
if (!email || !password) {
return res.json({ message: "All fields required" })
}
const user = await User.findOne({ email })
if (!user) {
return res.json({ message: "Incorrect email or password" })
}
const auth = await bcrypt.compare(password, user.password)
if (!auth) {
return res.json({ message: "Incorrect email or password" })
}
const token = createSecretToken(user._id);
res.cookie("token", token, {
withCredentials: true,
httpOnly: false,
});
res.status(201).json({ message: "User logged in successfully", success: true });
next()
} catch (error) {
console.log(error)
}
}
这是服务器上的index.js
const express = require('express');
const mongoose = require('mongoose');
const bcrypt = require('bcrypt')
const cookieParser = require("cookie-parser");
const bodyParser = require("body-parser")
const path = require('path')
const cors = require('cors')
require('dotenv').config();
const app = express();
const classRoutes = require('./routes/courseRoutes')
const authRoute = require("./routes/AuthRoutes");
const PORT = process.env.PORT || 4000;
mongoose.connection.on('connected', () => {
console.log('Connected to MongoDB Atlas');
});
app.use((req, res, next) => {
res.header("Access-Control-Allow-Origin", "https://divcourses.vercel.app");
res.header("Access-Control-Allow-Credentials", "true");
res.header("Access-Control-Allow-Methods", "GET,HEAD,OPTIONS,POST,PUT");
res.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept, Authorization");
next();
});
app.use(express.json());
app.use(express.urlencoded({ extended: true }));
app.use(cookieParser());
app.use('/api', classRoutes)
app.use('/api', authRoute);
app.use((req, res, next) => {
console.log(`${req.method} ${req.url}`);
next();
});
const atlasConnectionUri = process.env.MONGODB_URL;
mongoose.connect(atlasConnectionUri, {
dbName: 'subjects'
});
app.get('/', async (req, res) => {
try {
res.status(200).json({ message: "Welcome to Home Route 🏠" })
} catch (error) {
res.status(500).json({ message: "Error in Home Route ❌" })
}
});
app.listen(PORT, () => {
console.log(`Server is Running at ${PORT}`);
});
这是reactjs上的登录路线
import React, { useState } from "react";
import { Link, useNavigate } from "react-router-dom";
import axios from "axios";
import { ToastContainer, toast } from "react-toastify";
const Login = () => {
const navigate = useNavigate()
const [inputValue, setInputValue] = useState({
email: "",
password: ""
})
const { email, password } = inputValue
const handleOnChange = (e) => {
const { name, value } = e.target
setInputValue({
...inputValue,
[name]: value
})
}
const handleError = (err) =>
toast.error(err, {
position: "bottom-left",
});
const handleSuccess = (msg) =>
toast.success(msg, {
position: "bottom-left",
});
const handleSubmit = async (e) => {
e.preventDefault()
try {
// `${baseURL}/login`
const { data } = await axios.post(
"https://mern-deploy-practice.onrender.com/api/login",
{ ...inputValue },
{ withCredentials: true }
)
console.log(data);
const { success, message } = data;
if (success) {
handleSuccess(message);
setTimeout(() => {
navigate("/");
}, 1000);
} else {
handleError(message);
}
} catch (error) {
console.log(error);
}
setInputValue({
...inputValue,
email: "",
password: "",
});
};
这是用户成功登录后应重定向到的主页路由
import { useEffect, useState } from "react";
import { useNavigate } from "react-router-dom";
import { useCookies } from "react-cookie";
import { Link } from "react-router-dom";
import axios from "axios";
import { ToastContainer, toast } from "react-toastify";
const Home = () => {
const [cookies, setCookies, removeCookie] = useCookies(['token']);
const [username, setUsername] = useState('')
const navigate = useNavigate()
useEffect(() => {
const verifyCookie = async () => {
try {
const response = await axios.post(
"https://mern-deploy-practice.onrender.com/api",
{},
{ withCredentials: true }
);
if (response && response.data) {
const { status, user } = response.data;
setUsername(user)
setCookies('token', cookies.token)
const greeted = localStorage.getItem('greeted');
if (!greeted) {
toast(`Hello ${user}`, { position: "top-right" });
localStorage.setItem('greeted', 'true');
}
} else {
console.error(error)
removeCookie("token");
navigate('/login');
}
} catch (error) {
console.error("Error verifying cookie:", error);
removeCookie("token");
navigate('/login'); ``
}
if (!cookies.token) {
navigate("/login");
return;
}
};
verifyCookie();
}, [cookies, navigate, setCookies, removeCookie]);
const Logout = () => {
removeCookie("token");
navigate("/signup");
};
这是由于对 cookie 跨域功能的混淆所致。
看起来后端服务器位于“https://mern-deploy-practice.onrender.com/api”,因此当设置 cookie 时,浏览器会针对 that 域存储该 cookie。
前端代码位于Vercel域上。从根本上来说,它无法读取通过对外部域的调用设置的 cookie,因为该 cookie 属于该外部域。不过,您可以调用该外部域,当您隐式调用时,浏览器将在幕后发送相关 cookie。但您只能读取/检查 JS 中由 Vercel 域“拥有”的 cookie。
如果这是可能的,那将是一场灾难,因为任何网站都可能窃取另一个网站拥有的 cookie!因此,有一个基本原则,即 JS 只能读取在 JS 本身提供服务的同一域上设置的 cookie。
这里的解决方案看起来很简单。甚至不要尝试在 JS 中检查 cookie。除非我缺少上下文,否则似乎没有必要。您知道他们已通过 auth HTTP req 成功登录。您可以放心,(即使您无法检查它们)浏览器会将 cookie 发送到由该域上的后端服务设置的外部域。
如果您出于某种原因需要某些数据,通常服务器会在某个 API 端点上返回这些数据,您可以调用它来获取它。