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

使用Passport.js Google OAuth2.0登录后无法访问受保护资源

问题解决:Passport.js Google OAuth2.0 跨域请求403错误及认证中间件使用说明

核心问题分析

你遇到的403错误,本质是跨域请求未携带认证会话Cookie,导致后端无法识别已登录用户;同时对Passport认证中间件的使用逻辑存在误解。


一、紧急修复:解决/login/success接口403错误

1. 前端:axios请求携带会话凭证

前端发起的axios请求默认不会携带Cookie,需要显式开启withCredentials:

useEffect(() => {
  axios.get("http://localhost:6046/login/success", { withCredentials: true })
    .then((response) => {
      console.log(response.data , "response data from /protected \n");
      setAccess(true);
      setText(response.data.user.displayName);
    }).catch((error) => {
      console.log(error , ' error happened while trying to access protectedResource endpoint')
    })
},[])

// 登出请求同样需要添加
const handleLogout = () => {
  axios.get("http://localhost:6046/logout", { withCredentials: true })
}

2. 后端:配置CORS允许凭证传递

后端必须明确允许前端域名的凭证请求,不能用*作为origin:

const cors = require('cors');
app.use(cors({
  origin: process.env.CLIENT_URL, // 例如"http://localhost:3000",需精确匹配前端地址
  credentials: true // 允许跨域传递Cookie
}));

二、Passport认证中间件使用说明

你不需要在所有路由前都添加passport.authenticate,但需满足以下前提:

1. 确保会话中间件正确配置

Passport依赖express-session存储用户会话,必须在Passport初始化前配置:

const session = require('express-session');
app.use(session({
  secret: process.env.SESSION_SECRET, // 用环境变量存储密钥,不要硬编码
  resave: false,
  saveUninitialized: false,
  cookie: {
    secure: process.env.NODE_ENV === 'production', // 生产环境必须设为true(仅HTTPS)
    httpOnly: true,
    sameSite: 'lax' // 跨域场景下推荐设为'lax',生产环境配合secure可设为'none'
  }
}));

// 必须在session之后初始化Passport
app.use(passport.initialize());
app.use(passport.session());

2. 封装通用认证中间件(可选但推荐)

对需要验证身份的路由,可封装一个中间件统一处理,避免重复代码:

// 通用认证检查中间件
const requireAuth = (req, res, next) => {
  if (req.isAuthenticated()) {
    return next();
  }
  res.status(403).json({ error: true, message: "Not Authorized" });
};

// 用中间件保护路由
app.get("/login/success", requireAuth, (req, res) => {
  res.status(200).json({
    error: false,
    message: "Successfully Logged In",
    user: req.user,
  });
});

3. 确认序列化/反序列化函数正确

Passport需要通过这两个函数将用户数据存入/取出会话:

passport.serializeUser((user, done) => {
  done(null, user.id); // 仅存储用户ID到会话,避免数据过大
});

passport.deserializeUser((id, done) => {
  // 实际项目中需根据ID从数据库查询用户
  User.findById(id, (err, user) => {
    done(err, user);
  });
});

三、额外注意点

你之前的/protected路由能拿到req.user,是因为它是从/authorize(已通过Passport认证)跳转而来,属于同一个请求链;但前端跳转后发起的axios请求是独立的新请求,必须通过Cookie传递会话信息。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.04 06:55:05