Passport Discord OAuth2适配MySQL替代MongoDB的schema实现方案咨询
实现方案
首先澄清核心误解:passport 没有强制要求 done 函数传入的 user 必须是 mongoose schema 实例,只要是包含业务所需字段的普通 JS 对象即可,完全可以搭配 MySQL 使用。
1. 首先创建MySQL用户表
你可以先建一张存储Discord授权用户信息的表,参考建表SQL:
CREATE TABLE `discord_users` ( `id` int UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY, `discord_id` varchar(32) NOT NULL UNIQUE COMMENT 'Discord用户唯一ID', `username` varchar(100) NOT NULL, `email` varchar(100) DEFAULT NULL, `avatar` varchar(255) DEFAULT NULL, `access_token` varchar(255) NOT NULL, `refresh_token` varchar(255) NOT NULL, `created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, `updated_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
2. 修改DiscordStrategy回调逻辑
你可以用mysql2或者Sequelize这类MySQL客户端/ORM来操作数据库,替换原有mongoose的逻辑,示例如下(以mysql2/promise为例):
const mysql = require('mysql2/promise'); // 初始化MySQL连接池 const pool = mysql.createPool({ host: process.env.MYSQL_HOST, user: process.env.MYSQL_USER, password: process.env.MYSQL_PASSWORD, database: process.env.MYSQL_DB, waitForConnections: true, connectionLimit: 10, queueLimit: 0 }); passport.use(new DiscordStrategy({ clientID: process.env.CLIENT_ID, clientSecret: process.env.CLIENT_SECRET, callbackURL: process.env.CLIENT_REDIRECT, scope: ['identify', 'email', 'guilds'] }, async (accessToken, refreshToken, profile, done) => { try { // 先查找用户是否已经存在 const [rows] = await pool.query('SELECT * FROM discord_users WHERE discord_id = ?', [profile.id]); let user; if (rows.length > 0) { // 用户存在,更新token await pool.query( 'UPDATE discord_users SET access_token = ?, refresh_token = ?, username = ?, email = ?, avatar = ? WHERE discord_id = ?', [accessToken, refreshToken, profile.username, profile.email, profile.avatar, profile.id] ); user = rows[0]; } else { // 用户不存在,新建记录 const [result] = await pool.query( 'INSERT INTO discord_users (discord_id, username, email, avatar, access_token, refresh_token) VALUES (?, ?, ?, ?, ?, ?)', [profile.id, profile.username, profile.email, profile.avatar, accessToken, refreshToken] ); user = { id: result.insertId, discord_id: profile.id, username: profile.username, email: profile.email, avatar: profile.avatar, accessToken, refreshToken }; } // 直接传普通用户对象即可,不需要mongoose实例 done(null, user); } catch (err) { done(err, null); } }))
3. 配套修改passport序列化与反序列化逻辑
这一步是保证session读写正常的关键,需要对应MySQL的用户字段修改:
// 序列化:把用户ID存到session passport.serializeUser((user, done) => { done(null, user.id); }); // 反序列化:从session拿ID到MySQL查用户完整信息 passport.deserializeUser(async (id, done) => { try { const [rows] = await pool.query('SELECT * FROM discord_users WHERE id = ?', [id]); if (rows.length === 0) return done(null, false); done(null, rows[0]); } catch (err) { done(err, null); } });
可选:用Sequelize ORM定义类schema模型
如果你习惯了mongoose的schema写法,可以用Sequelize来定义MySQL的用户模型,功能和mongoose schema对等,示例如下:
const { Sequelize, DataTypes } = require('sequelize'); const sequelize = new Sequelize(process.env.MYSQL_DB, process.env.MYSQL_USER, process.env.MYSQL_PASSWORD, { host: process.env.MYSQL_HOST, dialect: 'mysql' }); const DiscordUser = sequelize.define('DiscordUser', { discord_id: { type: DataTypes.STRING(32), allowNull: false, unique: true }, username: { type: DataTypes.STRING(100), allowNull: false }, email: DataTypes.STRING(100), avatar: DataTypes.STRING(255), access_token: DataTypes.STRING(255), refresh_token: DataTypes.STRING(255) }, { tableName: 'discord_users', timestamps: true, createdAt: 'created_at', updatedAt: 'updated_at' }); // 同步模型到数据库(首次运行执行一次即可) // DiscordUser.sync({ alter: true });
后续操作直接调用Sequelize提供的findOne、create、update方法即可,写法和mongoose高度相似。
不需要把mongoose schema存储到MySQL,直接按上述方案迁移为MySQL的表结构/Sequelize模型即可,整个流程完全不需要依赖MongoDB。
内容的提问来源于stack exchange,提问作者olios
相关产品推荐
相关产品推荐

