You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Express+Passport.js后端在React客户端返回认证失败,浏览器正常

问题描述

搭建了集成Passport.js的Express后端,/check-auth接口在浏览器直接访问时返回authenticated: true,但通过React前端Axios发起请求时却返回authenticated: false,怀疑是req.isAuthenticated()的使用存在问题,相关代码如下:

Express后端代码

const express = require("express"); 
const mongoose = require("mongoose"); 
const cors = require("cors") 
require("dotenv").config() 
const session  = require("express-session");
const MongoStore = require('connect-mongo');
const passport = require("passport") 
const LocalStrategy  = require("passport-local").Strategy; 
const bcrypt = require("bcrypt"); 

const app = express()  

app.use(cors({ origin:'http://localhost:3000', credentials: true }));

mongoose.connect(process.env.DATABASE_URI) 
    .then(()=> console.log("connected to the Database") ) 
    .catch(error => console.log(error)) 

app.use(session({ 
    secret: 'thekey',
    resave: true,
    saveUninitialized: true,
    store:MongoStore.create({mongoUrl:process.env.DATABASE_URI}),
    cookie: {
      maxAge: 24 * 60 * 60 * 1000, // 1 day
    },
}));

app.use(passport.initialize()); 
app.use(passport.session());  

app.use(express.urlencoded({ extended: true }));
app.use(express.json())  

// 模型定义部分省略...

// Passport配置
passport.serializeUser((user, done) => {  
        done(null, user.id);
});

passport.deserializeUser(async (id, done) => { 
    try {  
        user = await User.findById(id); 
        if(!user) {
            return done(new Error('User not found'));
        }
        done(null, user)
    } catch (err) {
            done(err)
    }
});

passport.use(new LocalStrategy(
    function(username, password, done) {
        User.findOne({ username: username })
          .then(user => {
            if (!user) {
              return done(null, false, { message: 'Incorrect username.' });
            }
            if (user.password !== password) {
                return done(null, false, { message: 'Incorrect password.' });
              }
             return done(null, user); 
          })
          .catch(err => {
            return done(err);
          });
      }
));

// 接口定义部分省略...

app.post('/signin', passport.authenticate('local'), (req, res) => {
    console.log('User:', req.user);
    res.json({ isAuthenticated: req.isAuthenticated(), user: req.user });  
});

app.get('/check-auth', (req, res) => {
    if (req.isAuthenticated()) {
      return res.json({ authenticated: true, user: req.user });
    } else {
     return res.json({ authenticated: false, user: null });
    }
});

app.listen(3400 , ()=> {
    console.log("port is running on port 3400")
}) 

React前端代码

const HomePage = (props) => { 
    const [courts , setCourts ] = useState([]) 
    const navigate = useNavigate();

    useEffect(()=>{
        const grabCourts = async () =>{
            const res = await axios.get(`http://localhost:3400/courts`)
            setCourts(res.data)  
        } 
        grabCourts()  
    },[])

    const [isAuth ,setIsAuth] = useContext(AuthContext)

    useEffect(() =>  { 
        const checkAuth = async () => {
            try{
                const res = await axios.get(`http://localhost:3400/check-auth`)
                .then((res)=>{  
                    console.log(res)
                })
            } 
            catch(err){
                console.log(`error : ${err}`)
            }
        }
        checkAuth()
    }, [] ) 

    return( 
        <div className="container" >  
          <Top/>
            <h1 className="top-heading">Top Courts</h1>
            <div className="cards">
                {  
            courts.slice(0,3).map((court)=>(
                    <div className="card" key="_id">
                        <h1>
                            {court.name}
                        </h1> 
                        <h2>
                            {court.address}
                        </h2>   
                        <div className="court-type">
                         <img src={bb} className="bb-image"></img> 
                         <p className="court-num">{court.courtCount} x</p> 
                        <Link to={`/courts/${court._id}`}>
                          <button className="read-more">More Info</button>
                        </Link>
                        </div>  
                    </div>
            ))}
            </div>
        </div>
    )
} 

export default HomePage

问题分析与解决方案

不是req.isAuthenticated()的使用问题,核心原因是跨域请求时Axios未携带Session Cookie:Passport.js依赖Session Cookie识别已认证用户,浏览器直接访问会自动携带Cookie,但跨域AJAX请求默认不会发送Cookie,导致后端无法识别用户会话。

修复步骤

1. 前端Axios开启凭证携带

修改checkAuth函数,在请求中添加withCredentials: true:

const checkAuth = async () => {
    try{
        const res = await axios.get(`http://localhost:3400/check-auth`, {
            withCredentials: true // 关键:携带Session Cookie
        })
        .then((res)=>{  
            console.log(res)
        })
    } 
    catch(err){
        console.log(`error : ${err}`)
    }
}

如果多个请求都需要携带Cookie,可全局配置Axios:

import axios from 'axios';
axios.defaults.withCredentials = true;

2. 后端Session Cookie配置优化

调整Session的Cookie属性,确保跨域场景下能正常传递:

app.use(session({ 
    secret: 'thekey',
    resave: false, // 建议改为false,减少不必要的会话更新
    saveUninitialized: false, // 建议改为false,仅在会话有数据时存储
    store:MongoStore.create({mongoUrl:process.env.DATABASE_URI}),
    cookie: {
        maxAge: 24 * 60 * 60 * 1000, // 1 day
        httpOnly: true, // 增强安全性,防止XSS攻击
        sameSite: 'lax', // 允许跨域请求携带Cookie
        // 生产环境HTTPS下需开启:
        // secure: process.env.NODE_ENV === 'production'
    },
}));

3. 额外验证点

  • 确认登录后浏览器已存储Session Cookie:在开发者工具的Application -> Cookies中查看localhost:3400下的会话Cookie。
  • 后端CORS配置已正确设置credentials: true(当前配置无误)。

内容的提问来源于stack exchange,提问作者Paul Morgan

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.28 08:47:36