Express + Passport JS环境下Session Cookie无法设置问题
在MERN栈应用中使用Passport JS实现用户认证,需要保护路由仅允许已认证用户访问。使用Thunder Client测试/login路由及受保护路由时功能正常,但在React前端登录成功后,Passport生成的Session Cookie虽出现在响应中,却未被浏览器设置,导致后续无法完成认证。
已尝试直接通过res.cookie("test", "tester")设置测试Cookie,这种方式可以正常生效,但Passport的Session Cookie始终无法被浏览器保存,且更换不同浏览器测试后问题依旧。
后端地址为localhost:3001,响应中存在Cookie但浏览器未设置的截图如下:

浏览器中无Cookie的状态:

Auth.mjs
import UsersModel from "../Models/User.mjs"; import bcrypt from "bcrypt"; import { omit, pick } from "../helpers.mjs"; // ---------------------------- // // ----- AUTH ROUTES ----- // // ---------------------------- // const auth = (app, checkAuthenticated, checkNotAuthenticated, passport) => { app.delete("/logout", checkAuthenticated, (req, res) => { req.logout((err) => { if (err) { return next(err); } res.json({ msg: "Logged out"}); }); }); // BAD ROUTE - cookie SHOULD be attached app.post("/login", checkNotAuthenticated, passport.authenticate("local"), (req, res) => { res.json({ msg: "Authenticated", user: req.user }) }); app.post("/register", checkNotAuthenticated, async (req, res) => { try { const hashedPassword = await bcrypt.hash(req.body.password, 15) await UsersModel.create({ name: req.body.name, password: hashedPassword, email: req.body.email }) res.json({ msg: "Registered new user"}) } catch { res.json({ msg: "Error"}) } }) } export default auth;
Passport.mjs
import passportLocal from "passport-local"; import bcrypt from "bcrypt"; const LocalStrategy = passportLocal.Strategy; const initialize = async (passport, getUserByEmail, getUserById) => { const authenticateUser = async (email, password, done) => { const user = await getUserByEmail(email); if (user == null) { return done(null, false, {msg: "No user with that email"}) console.log("LOL") } try { console.log(`pass: ${password}, hash: ${user.password}`) if (await bcrypt.compare(password, user.password)) { console.log(user) return done(null, user); } else { return done(null, false, {msg: "Password incorrect"}) } } catch (e) { return done(e) } } passport.use(new LocalStrategy({ usernameField: "email"}, authenticateUser)) passport.serializeUser((user, done) => done(null, user._id)); passport.deserializeUser(async (id, done) => done(null, await getUserById(id))); } export default initialize;
主服务器文件
import dotenv from "dotenv"; if (process.env.NODE_ENV !== "production") { dotenv.config(); } import express from "express"; import mongoose from 'mongoose'; import cors from 'cors'; import UsersModel from './Models/User.mjs'; import passport from "passport"; import flash from "express-flash"; import session from "express-session"; import initialize from "./Routes/Passport.mjs"; import mongoStore from 'express-session-mongo'; import cookieParser from "cookie-parser"; // route imports import items from "./Routes/Items.mjs"; import departments from "./Routes/Departments.mjs"; import houses from "./Routes/Houses.mjs"; import lists from "./Routes/Lists.mjs"; import people from "./Routes/People.mjs"; import auth from "./Routes/Auth.mjs"; const getUserByEmail = async (email) => { return await UsersModel.findOne({email: email}); } const getUserById = async (id) => { return await UsersModel.findById(id); } // new express app const app = express() // ---------------------------- // // ----- MIDDLEWARE ----- // // ---------------------------- // app.use(cors({credentials: true})) app.use(express.json()) app.use(express.urlencoded({ extended: true })); app.use(flash()) app.use(cookieParser()) app.use(session({ secret: process.env.SESSION_SECRET, resave: false, saveUninitialized: false, })) app.use(passport.initialize()) app.use(passport.session()) // connect to mongoDB mongoose.connect(process.env.DB) // initialize passport initialize(passport, getUserByEmail, getUserById) // check auth/not auth const checkAuthenticated = (req, res, next) => { if (req.isAuthenticated()) { return next(); } // ALWAYS HITS BELOW \/ return res.status(401).json({msg: "Must be authenticated"}); } const checkNotAuthenticated = (req, res, next) => { if (req.isAuthenticated()) { return res.json({msg: "Can't access, authenticated"}); } return next(); } // ---------------------------- // // ----- ROUTES ----- // // ---------------------------- // items(app, checkAuthenticated, checkNotAuthenticated); departments(app, checkAuthenticated, checkNotAuthenticated); houses(app, checkAuthenticated, checkNotAuthenticated); lists(app, checkAuthenticated, checkNotAuthenticated); people(app, checkAuthenticated, checkNotAuthenticated); auth(app, checkAuthenticated, checkNotAuthenticated, passport); // ---------------------------- // // ----- START SERVER ----- // // ---------------------------- // app.listen(process.env.PORT, () => { console.log("--- Server is UP and running ---") }) // ---------------------------- // // ---------------------------- //
React前端代码
import React, { useContext, useState } from 'react'; import "../css/Auth.css"; import { useMutation } from '@tanstack/react-query'; import { Context } from '../App'; const Authenticate = () => { const [isLogin, setIsLogin] = useState(false) const [loginEmail, setLoginEmail] = useState(""); const [loginPwd, setLoginPwd] = useState(""); const [registerName, setRegisterName] = useState(""); const [registerEmail, setRegisterEmail] = useState(""); const [registerPwd, setRegisterPwd] = useState(""); const [registerPwdConfirm, setRegisterPwdConfirm] = useState(""); const {user, authenticated} = useContext(Context); const [userVal, setUserVal] = user; const [authenticatedVal, setAuthenticatedVal] = authenticated; const fetchLoginQuery = async () => { const req = await fetch(`${import.meta.env.VITE_REACT_APP_API}/login`, { method: 'post', headers: { "Content-Type": "application/json", }, body: JSON.stringify({ email: loginEmail, password: loginPwd }) }); return req.json(); } const fetchRegisterQuery = async () => { const req = await fetch(`${import.meta.env.VITE_REACT_APP_API}/register`, { method: 'post', headers: { "Content-Type": "application/json", }, body: JSON.stringify({ email: loginEmail, password: loginPwd }) }); return req.json(); } const loginMutation = useMutation({ mutationFn: fetchLoginQuery, onSuccess: (data) => { if (data.msg == "Authenticated") { setAuthenticatedVal(true); setUserVal(data.user) } // queryClient.invalidateQueries({ queryKey: [""]}) } }) const registerMutation = useMutation({ mutationFn: fetchRegisterQuery, onSuccess: () => { // queryClient.invalidateQueries({ queryKey: [""]}) } }) const handleLogin = () => { loginMutation.mutate(); } const handleRegister = () => { //TODO } return ( <div> <h1>Welcome to the Grocery List App</h1> <button onClick={() => setIsLogin(!isLogin)}>{isLogin ? "Create an account?" : "Log in to existing account"}</button> {isLogin ? <> <input type="text" placeholder="email" value={loginEmail} onChange={(e) => {setLoginEmail(e.target.value)}}/> <input type="text" placeholder="password" value={loginPwd} onChange={(e) => {setLoginPwd(e.target.value)}}/> <button onClick={handleLogin}>Login</button> </> : <> <input type="text" placeholder="name" value={registerName} onChange={(e) => {setRegisterName(e.target.value)}}/> <input type="email" placeholder="email" value={registerEmail} onChange={(e) => {setRegisterEmail(e.target.value)}}/> <input type="text" placeholder="password" value={registerPwd} onChange={(e) => {setRegisterPwd(e.target.value)}}/> <input type="text" placeholder="password again" value={registerPwdConfirm} onChange={(e) => {setRegisterPwdConfirm(e.target.value)}}/> <button onClick={handleRegister}>Register</button> </> } </div> ) } export default Authenticate;
内容的提问来源于stack exchange,提问作者jc22920
相关产品推荐
相关产品推荐

