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

Express+React中Passport-Local认证重定向遇CORS错误,Google-OAuth正常

Express + React 项目中 Passport-Local 认证重定向触发CORS问题

在Express与React项目里,使用Passport-Google-OAuth20策略完成认证后可以正常重定向到前端,但使用Passport-Local本地认证策略时,重定向操作会触发CORS错误。

后端代码

import { config } from "dotenv"
import express,{Request,Response} from "express"
import mongoose, {ConnectOptions, Error} from "mongoose";
import DeckModal from './models/deck';
import cors from "cors"
import session from "express-session";
import passport from "passport";
import User from "./models/user";
import bcrypt from "bcryptjs"
import { UserDetails } from "./types/UserDetails";
import passportLocal from "passport-local"

const LocalStrategy=passportLocal.Strategy
const MongoDBStore = require('connect-mongodb-session')(session);
const GoogleStrategy =require("passport-google-oauth20")
config()

let PORT:number=5000

const app = express()

app.use(express.json())

const  store = new MongoDBStore({
  uri: process.env.MONGO_URI!,
  collection: 'mysessions'
});

store.on('error', function(error:Error) {
  console.log(error);
});

app.use(cors({ origin:"http://localhost:5173" ,credentials:true}))

app.set('trust proxy', 1) 

// 创建session
app.use(session({
  secret:process.env.SESSION_SECRET!,
  resave:true,
  saveUninitialized:true,
  store:store,
  // cookie:{
  //   httpOnly:true, //HttpOnly Cookie 可防止客户端脚本访问cookie数据
  //   sameSite:"none", // 前后端跨域时需设置sameSite:none以共享cookie
  //   secure:true,      // 仅允许HTTPS请求
  //   maxAge: 1000 * 60 * 60 * 24 * 7
  // }
 
}))

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

// Local认证策略
passport.use(new LocalStrategy(  
  {usernameField:"email"},
  function(username, password, done) {
    User.findOne({ email: username }, function (err:Error, user:UserDetails) {
      if (err) { return done(err); }  // 发生错误时返回错误信息
      if (!user) { return done(null, false); } // 未找到用户时返回null和false
      bcrypt.compare(password,user.password!,(err,result)=>{
        if(err) throw err 
        if(result===true){
          return done(null,user) 
        }
        else{
          return done(null,false) 
        }
      })
    });
  }
));

// Google认证策略
passport.use(
  new GoogleStrategy(
    {
      callbackURL: "/auth/google/callback",
      clientID: process.env.CLIENT_ID,
      clientSecret: process.env.CLIENT_SECRET,
    },
    async (accessToken:any, refreshToken:any, profile:any, done:any) => {
      const userInfo:UserDetails={
        email:profile.emails[0].value,
        profile_picture:profile.photos[0].value,
        username:profile.displayName
      }

      try{
        const user= await User.findOne({ email: profile.emails[0].value })
        if(!user){
           const newUser= new User(userInfo)
           await newUser.save()
           return done(null,newUser)
       }
       return done(null,user)
      }
      catch(err){
         return done(null,false,err)
      }
        
    }
  )
);

passport.serializeUser((user:any,done)=>{
  return done(null,user._id)
 })

 passport.deserializeUser((id:string,done)=>{
   User.findById(id,(err:Error,doc:any)=>{
    const userInfo={
      username:doc.username, 
      email:doc.email,
      profile_picture:doc.profile_picture,
    }
     return done(null,userInfo)
   })
 })

// 连接MongoDB数据库
mongoose.connect(process.env.MONGO_URI!,{
  useUnifiedTopology: true,
  useNewUrlParser: true
} as ConnectOptions )
.then(()=>{
  console.log("Connected to mongodb successfully");
  // 数据库连接成功后启动服务
  app.listen(process.env.PORT || PORT,()=>{
    console.log("server is running on port "+PORT)
  })
})
.catch((err:Error)=>{console.log(err)});


// Google认证路由
app.get('/auth/google', passport.authenticate('google', {
  scope: ['profile','email']
}));

app.get('/auth/google/callback',
  passport.authenticate('google', { failureRedirect: "http://localhost:5173/login", failureMessage: true,successRedirect:"http://localhost:5173/auth/success"}));

app.get("/getuser",(req,res)=>{
  if(req.user){
    res.json(req.user) // Passport的deserializeUser会自动将用户信息附加到req.user
  }
  else{
    res.json(null)
  }
 })

app.post('/auth/logout', function(req, res, next) {
  req.logout(function(err) {
    if (err) { return next(err); }
    res.send('done');
  });
});


// 本地认证路由
app.post("/signup",async(req:Request,res:Response)=>{
 const {username,email,password}=req?.body
 if(!username || !password || !email){
  res.send("请提供用户名、邮箱和密码")
  return
 }
 try{
  const user:UserDetails | null=await User.findOne({username})
  if(!user){
   const hashedPassword=await bcrypt.hash(password,10) 
   const newUser= new User({
     username,
     email,
     password:hashedPassword
   })
   await newUser.save()
   res.send("注册成功")
  }
  else{
   res.send("用户已存在")
  }
 }
 catch(error){
  console.log(error)
 }
})

app.post('/login', 
  passport.authenticate('local'),
  (req, res) => {
    if(req.user){
      res.redirect("http://localhost:5173/auth/success")
    }
    else{
      res.status(200).json({success:false});
    }
  }
);

尝试过的修改

我曾尝试直接在passport.authenticate中配置successRedirect,但依然出现相同错误:

app.post('/login', 
passport.authenticate('local',{successRedirect:"http://localhost:5173/auth/success"}));

错误信息

Access to XMLHttpRequest at 'http://localhost:5173/' (redirected from 'http://localhost:5000/login') from origin 'http://localhost:5173' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: The value of the 'Access-Control-Allow-Origin' header in the response must not be the wildcard '*' when the request's credentials mode is 'include'. The credentials mode of requests initiated by the XMLHttpRequest is controlled by the withCredentials attribute.

前端请求代码

本地策略登录

const handleLogin = async (e:FormEvent<HTMLFormElement>) => {
  e.preventDefault()
  
    const data = {  email, password };
     
    axios.post("http://localhost:5000/login",data,{withCredentials:true})

}

Google策略登录

const handleGoogleLogin = async () => {
    window.open("http://localhost:5000/auth/google","_self")
}

内容的提问来源于stack exchange,提问作者Max Vhanamane

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.08 11:35:19