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

如何让Express服务器向React客户端传递Discord OAuth2用户信息?

解决Discord OAuth2登录后用户信息传递到React客户端的问题

你当前的核心问题是:通过<a>标签跳转触发OAuth流程后,后端返回的JSON无法被React客户端捕获——因为这是浏览器直接导航的请求,而非前端发起的AJAX请求。下面提供两种实用方案,结合你的代码进行修改:


这种方式更安全,通过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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.29 14:17:56