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

Express应用中Passport Google OAuth登录后用户对象不更新问题

问题:Passport Google OAuth登录后前端获取的user对象始终不变

我在Express应用中用Passport Google OAuth实现用户登录功能,每次登录都能成功获取到用户资料,但回调函数返回正确用户资料的情况下,发送给前端的user对象却始终没有变化。已经尝试在前后端删除Cookie,但问题依然存在。


passport策略文件(passport-google.js)

const User = require('./models/auth')
const passport = require('passport')
const GoogleStrategy = require('passport-google-oauth20').Strategy;

passport.use( new GoogleStrategy({
   clientID: process.env.GOOGLE_CLIENT_ID,
   clientSecret: process.env.GOOGLE_CLIENT_SECRET,
   callbackURL: '/auth/google/callback', 
   passReqToCallback: true, 
}, (req, accessToken, refreshToken, profile, done) => {
   User.findOne({googleID: profile.id})
       .then((currentUser) =>{
           if(currentUser){
               // 用户已存在
               return done(null, currentUser)
           }else{
               // 创建新用户
               new User({
                   username: profile.displayName,
                   googleID: profile.id,
                   email: profile.emails[0].value
               })
               .save()
               .then((savedUser) => {
                   // console.log(savedUser)
                   return done(null, savedUser)
               })
           }
       }).catch((err) => {
           console.log('User not found!', err)
       })
}))

passport.serializeUser((user, cb)=>{
   console.log('users: ', user)
   return cb(null, user.id)
})

passport.deserializeUser(async(id, cb) =>{
   console.log('id: ' , id)
   const user = User.findOne({where: {_id: id}})
   .then((user)=>{
       cb(null, user)
   })
   .catch((err)=>{
       console.log(err)
       cb(err, null)
   })
})

路由与控制器代码(authRouter.js)

const router = require('express').Router()
const passport = require('passport')

router.get('/logout', async(req, res) =>{
    if(req.user){
        req.logout()
        res.clearCookie("session", {path:"/",httpOnly:true})
        res.clearCookie("session.sig", {path:"/",httpOnly:true})
     
        return res.redirect('http://localhost:3000/')
    }else{
        res.status(200).send('User already logged out!')
    }
})


router.get('/login/success', async(req, res) => {
    if(req.user){
        res.status(200).json({
            success: true,
            message: 'Login success',
            user: req.user,
        })
    }else{
        res.status(401).json({
            success: false,
            message: 'You are not logged in',
        })
    }
})

router.get('/login/failed', (req, res) => {
    res.status(404).json({
        success: false,
        message: 'Login failed',
        user: false
    })
})

router.get('/google', passport.authenticate('google', {session: false ,scope: ['profile', 'email']}))

router.get('/google/callback', passport.authenticate('google',  {
        failureRedirect: '/login/failed',
        failureMessage: 'Login with Google failed! Try again.', 
        // session: false
    }), (req, res) =>{
    res.redirect('http://localhost:3000/', )
})

module.exports = router

app.js

require('dotenv').config()
const connectDB = require('./database/connect')
const express = require('express')
const cors = require('cors')
const cookieSession = require('cookie-session')
const passport = require('passport')
// const passportSetup = require('./passport')
const passportSetupStrategy = require('./passport-google')
const authRouter = require('./routers/authRouter')
const app  = express()

app.use(cookieSession({name: 'session', keys: [process.env.SESSION_KEY] ,maxAge: 24 * 60 * 60 * 1000}))
app.use(cors({
    origin: 'http://localhost:3000',
    credentials: true,
    methods: 'GET,PUT,POST,PATCH'
}))

// 编码中间件
app.use(express.json())
app.use(express.static('public'))
app.use(express.urlencoded({extended: false}))

app.use(passport.initialize())
app.use(passport.session())
app.use('/auth', authRouter)

const mongoURI = process.env.MONGO_URI

const startServer = async() => {
    try {
        await connectDB(mongoURI)
        app.listen('5000', async() => {
            console.log('Server listening on port 5000')
        })
    } catch (error) {
        console.log(error)
    }
}

startServer()

前端代码

App.js

import logo from './logo.svg';
import './App.css';
import { useEffect, useState } from 'react';
import Navbar from './components/Navbar/Navbar';
import { useDispatch } from 'react-redux';
import { getMovies } from './features/movie/movieSlice';
import {Routes, Route} from 'react-router-dom'
import { Home } from './pages/Home/Home';
import { MovieDetail } from './pages/MovieDetail/MovieDetail';
import Login from './pages/Login/Login';
import { setUser, clearUser } from './features/auth/authSlice';
import axios from 'axios';


function App() {
  const [isLoading, setIsLoading] = useState()
  const dispatch = useDispatch()
    useEffect(() => {
        dispatch(getMovies())
        const getUser = async() =>{
            fetch('http://localhost:5000/auth/login/success', {
              credentials: 'include',
              method: 'GET',
              headers: {
                Accept: 'application/json',
                'Content-Type': 'application/json',
                'Access-Control-Allow-Credential': true,
              },
            })
            .then((res)=>{
              return res.json()
            })
            .then((data) =>{
              console.log('received: ', data)
              dispatch(setUser(data.user))
            })
          .catch((error)=>{
              console.log(error)
              dispatch(clearUser())
          })
      }
      getUser()
    }, [])

  return (
    <div className='flex flex-col gap-5 relative bg-indigo-900'>
      <Navbar />
      <Routes>
        <Route path='/' element={<Home />}/>
        <Route path='/movie/:id' element={<MovieDetail />}/>
        <Route path='/login' element={<Login />}/>
      </Routes>
    </div>
  );
}

export default App;
import React from 'react'
import { FaHeart } from 'react-icons/fa'
import { useDispatch, useSelector } from 'react-redux'
import { Link } from 'react-router-dom'
import { clearUser } from '../../features/auth/authSlice'
import Favourites from '../Favourites/Favourites'
import './Navbar.css'

const Navbar = () => {
  const {user} = useSelector(store => store.auth)
  const dispatch = useDispatch()
  const logout = () =>{
    window.open('http://localhost:5000/auth/logout', '_self')
    document.cookie = null
    dispatch(clearUser())
  }

  return (
    <header className='header text-white flex justify-between items-center p-2 bg-indigo-500 lg:px-20'>
        <Link 
          className='text-center text-3xl font-bold'
          to='/'
        >
            <span className='bg-white text-indigo-900 p-2 shadow-lg shadow-black rounded-md'>M</span>Moviex
        </Link>
        {
          user? (
            <div className='flex gap-1 items-center'>
                <p>Hello, {user.username}</p>
                <Link 
                  className='bg-slate-400 p-2 rounded-md' 
                  onClick={logout}>Logout</Link>
            </div>
          ):( 
            <Link to='/login'>Login</Link>
          )
        }
        <Favourites />
    </header>
  )
}

export default Navbar

解决方案

1. 修复Google登录路由的session配置矛盾

在authRouter.js中,/auth/google路由设置了session: false,但后续回调又启用了session,导致登录时无法正确创建会话,后续请求拿不到最新用户。删除该选项:

// 修改前
router.get('/google', passport.authenticate('google', {session: false ,scope: ['profile', 'email']}))
// 修改后
router.get('/google', passport.authenticate('google', {scope: ['profile', 'email']}))

2. 修复deserializeUser中的查询语法错误

Mongoose的findOne不需要where关键字,错误的语法会导致查询不到用户,返回旧的会话数据。修改passport-google.js中的deserializeUser:

// 修改前
const user = User.findOne({where: {_id: id}})
// 修改后
const user = User.findOne({_id: id})

3. 前端添加路由监听,确保登录后重新获取用户

App.js中的getUser只在组件挂载时执行一次,登录回调跳转后不会主动刷新用户数据。添加路由监听,每次路由变化时重新获取用户:

import { useLocation } from 'react-router-dom';

// ...
function App() {
  const [isLoading, setIsLoading] = useState()
  const dispatch = useDispatch()
  const location = useLocation() // 新增

  useEffect(() => {
      dispatch(getMovies())
      const getUser = async() =>{
          // 原getUser逻辑不变
      }
      getUser()
  }, [location.pathname]) // 依赖改为路由路径

  // ...
}

4. 修正前端logout的Cookie处理逻辑

前端document.cookie = null无法正确删除Cookie,后端已经在注销时清除会话Cookie,直接去掉这行代码即可:

// 修改前
const logout = () =>{
    window.open('http://localhost:5000/auth/logout', '_self')
    document.cookie = null
    dispatch(clearUser())
}
// 修改后
const logout = () =>{
    window.open('http://localhost:5000/auth/logout', '_self')
    dispatch(clearUser())
}

内容的提问来源于stack exchange,提问作者Bello Shehu

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.29 15:47:09