如何让Express服务器向React客户端传递Discord OAuth2用户信息?
解决Discord OAuth2登录后用户信息传递到React客户端的问题
你当前的核心问题是:通过<a>标签跳转触发OAuth流程后,后端返回的JSON无法被React客户端捕获——因为这是浏览器直接导航的请求,而非前端发起的AJAX请求。下面提供两种实用方案,结合你的代码进行修改:
方案一:HttpOnly Cookie + 后端接口(生产环境推荐)
这种方式更安全,通过Cookie存储用户信息,前端通过接口获取,避免敏感信息暴露。
步骤1:后端配置依赖与中间件
首先安装必要依赖:
npm install cookie-parser cors
修改index.js,添加Cookie解析和跨域配置:
const express = require("express"); const cookieParser = require('cookie-parser'); const cors = require('cors'); const app = express(); // 解析Cookie app.use(cookieParser()); // 配置跨域,允许前端端口携带Cookie app.use(cors({ origin: 'http://localhost:3000', credentials: true })); const discordRouter = require('./api/discord'); app.use('/api/discord', discordRouter); // 错误处理和监听代码不变 app.use((err, req, res, next) => { switch (err.message) { case 'NoCodeProvided': return res.status(400).send({ status: 'ERROR', error: err.message, }); default: return res.status(500).send({ status: 'ERROR', error: err.message, }); } }); app.listen(47044, () => { console.log(`Server listening on 47044`); });
步骤2:修改后端Discord回调路由
更新discord.js,设置Cookie并重定向到前端,同时添加获取用户信息的接口:
const express = require('express'); const { catchAsync } = require('../utils'); const router = express.Router(); const { clientId, clientSecret, port } = require('../config.json'); const redirect = `http://localhost:${port}/api/discord/callback`; router.get('/login', (req, res) => { res.redirect(`https://discord.com/api/oauth2/authorize?response_type=code&client_id=${clientId}&scope=identify&redirect_uri=${redirect}`); }); router.get('/callback', catchAsync(async (req, res) => { const data = { client_id: clientId, client_secret: clientSecret, grant_type: 'authorization_code', redirect_uri: redirect, code: req.query.code, scope: ['identify', 'email', 'guilds'], }; const response = await fetch('https://discord.com/api/oauth2/token', { method: 'POST', body: new URLSearchParams(data), headers: { 'Content-Type': 'application/x-www-form-urlencoded', } }) const json = await response.json(); const fetchDiscordUserInfo = await fetch('https://discord.com/api/users/@me', { headers: { Authorization: `Bearer ${json.access_token}`, } }); const user = await fetchDiscordUserInfo.json(); // 设置HttpOnly Cookie,防止XSS攻击,有效期1天 res.cookie('discord_user', JSON.stringify(user), { httpOnly: true, secure: process.env.NODE_ENV === 'production', // 生产环境启用HTTPS时打开 maxAge: 24 * 60 * 60 * 1000 }); // 重定向回前端首页 res.redirect('http://localhost:3000'); })); // 添加接口供前端获取用户信息 router.get('/user', catchAsync(async (req, res) => { if (!req.cookies.discord_user) { return res.status(401).json({ loggedIn: false }); } const user = JSON.parse(req.cookies.discord_user); res.json({ loggedIn: true, user }); })); module.exports = router;
步骤3:前端修改获取用户信息
更新NavBar.js,在组件挂载时请求后端接口获取用户状态:
import React from 'react'; import { Link } from 'react-router-dom'; import logo from './path-to-logo'; // 替换为你的logo路径 class NavBar extends React.Component{ constructor(props) { super(props); this.state = { user: null, isLoggedIn: false }; } componentDidMount() { // 请求后端接口获取用户信息,必须携带credentials fetch('http://localhost:47044/api/discord/user', { credentials: 'include' }) .then(res => res.json()) .then(data => { if (data.loggedIn) { this.setState({ user: data.user, isLoggedIn: true }); } }) .catch(err => console.error('获取用户信息失败:', err)); } render() { const { isLoggedIn, user } = this.state; return ( <nav className="navbar nav-background"> <ul className="nav-list"> <Link className="logo" to="/home"> <img src={logo} alt='img'/> </Link> <li><Link to="/bot" className="nav-link">MessageShark Bot</Link></li> <li><Link to="/join" className="nav-link">Join Our Discord</Link></li> <li><Link to="/command" className="nav-link">Command</Link></li> <li><Link to="/help" className="nav-link">Help</Link></li> </ul> <div className="rightNav"> {isLoggedIn ? ( <span className="user-info">欢迎, {user.username}#{user.discriminator}</span> ) : ( <a className="btn btn-oauth" href='http://localhost:47044/api/discord/login' >Login with Discord</a> )} </div> </nav> ); } } export default NavBar;
方案二:URL参数传递(仅测试环境使用)
这种方式简单直接,但用户信息会暴露在URL中,存在安全风险,仅适合本地测试。
修改后端回调路由
更新discord.js的/callback路由:
router.get('/callback', catchAsync(async (req, res) => { // ... 前面获取user的代码不变 // 编码用户信息后拼接到URL参数 const encodedUser = encodeURIComponent(JSON.stringify(user)); res.redirect(`http://localhost:3000?user=${encodedUser}`); }));
前端读取URL参数
在前端首页组件(如Home.js)中读取参数并保存状态:
componentDidMount() { const urlParams = new URLSearchParams(window.location.search); const userStr = urlParams.get('user'); if (userStr) { const user = JSON.parse(decodeURIComponent(userStr)); this.setState({ user }); // 清除URL参数,避免刷新后重复显示 window.history.replaceState({}, document.title, window.location.pathname); } }
内容的提问来源于stack exchange,提问作者Eric Kim
相关产品推荐
相关产品推荐

