使用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
相关产品推荐
相关产品推荐

