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;
Navbar.jsx
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
相关产品推荐
相关产品推荐

