新手求教:基于Node.js、MongoDB和Express实现Passport OAuth2认证REST API
使用Passport OAuth2为Node.js + Express + MongoDB REST API实现认证的分步指南
嘿,作为Node.js新手,用Passport OAuth2给REST API做认证确实得一步步来,我给你整理了超详细的实操步骤,跟着做应该能顺利搞定!
1. 初始化项目并安装依赖
首先咱们搭好基础环境,打开终端创建项目文件夹,然后初始化:
mkdir oauth2-api-auth && cd oauth2-api-auth npm init -y
接下来安装核心依赖,包括Express、MongoDB驱动、Passport和OAuth2策略,还有环境变量管理和跨域支持:
npm install express mongoose passport passport-oauth2 dotenv cors jsonwebtoken
再装个开发依赖方便热重载:
npm install -D nodemon
记得在package.json里加个启动脚本:
"scripts": { "start": "node server.js", "dev": "nodemon server.js" }
2. 搭建项目结构
咱们把文件分模块放,结构清晰好维护:
oauth2-api-auth/ ├── config/ # 配置文件 │ ├── db.js # MongoDB连接配置 │ └── passport.js # Passport OAuth2策略配置 ├── models/ # 数据模型 │ └── User.js # 用户模型 ├── routes/ # 路由文件 │ ├── auth.js # 认证相关路由 │ └── api.js # 受保护的API路由 ├── .env # 环境变量 └── server.js # 服务器入口文件
3. 配置MongoDB连接
在config/db.js里写MongoDB的连接逻辑,用Mongoose来操作数据库:
const mongoose = require('mongoose'); require('dotenv').config(); const connectDB = async () => { try { await mongoose.connect(process.env.MONGODB_URI); console.log('MongoDB连接成功!'); } catch (err) { console.error('MongoDB连接失败:', err.message); process.exit(1); } }; module.exports = connectDB;
4. 创建用户数据模型
在models/User.js里定义用户Schema,存储OAuth2提供商返回的用户信息:
const mongoose = require('mongoose'); const userSchema = new mongoose.Schema({ oauthId: { type: String, required: true, unique: true }, provider: { type: String, required: true, enum: ['github', 'google', 'custom'] // 根据你的提供商调整 }, email: { type: String, required: true, unique: true }, displayName: { type: String, required: true } }, { timestamps: true }); module.exports = mongoose.model('User', userSchema);
5. 配置Passport OAuth2策略
这是核心步骤,在config/passport.js里设置OAuth2的认证逻辑:
const passport = require('passport'); const OAuth2Strategy = require('passport-oauth2').Strategy; const User = require('../models/User'); const jwt = require('jsonwebtoken'); require('dotenv').config(); // 配置OAuth2策略 passport.use(new OAuth2Strategy({ authorizationURL: process.env.OAUTH2_AUTH_URL, // 提供商的授权地址(比如GitHub的https://github.com/login/oauth/authorize) tokenURL: process.env.OAUTH2_TOKEN_URL, // 提供商的令牌地址(比如GitHub的https://github.com/login/oauth/access_token) clientID: process.env.OAUTH2_CLIENT_ID, // 你的客户端ID(从提供商后台获取) clientSecret: process.env.OAUTH2_CLIENT_SECRET, // 你的客户端密钥 callbackURL: process.env.OAUTH2_CALLBACK_URL // 回调地址(要和提供商后台配置的一致) }, async (accessToken, refreshToken, profile, done) => { try { // 先查找是否已有该用户 let user = await User.findOne({ oauthId: profile.id, provider: process.env.OAUTH2_PROVIDER }); if (!user) { // 没有的话创建新用户 user = await User.create({ oauthId: profile.id, provider: process.env.OAUTH2_PROVIDER, email: profile.emails[0].value, displayName: profile.displayName }); } // 生成JWT令牌(REST API常用无状态认证) const token = jwt.sign({ id: user._id }, process.env.JWT_SECRET, { expiresIn: '7d' }); // 把用户和token传给回调 done(null, { user, token }); } catch (err) { done(err, null); } })); // 序列化用户(REST API用JWT的话,这里可以简化) passport.serializeUser((user, done) => { done(null, user.id); }); // 反序列化用户 passport.deserializeUser(async (id, done) => { try { const user = await User.findById(id); done(null, user); } catch (err) { done(err, null); } }); module.exports = passport;
6. 启动Express服务器并配置中间件
在server.js里整合所有配置,启动服务器:
const express = require('express'); const connectDB = require('./config/db'); const passport = require('./config/passport'); const cors = require('cors'); require('dotenv').config(); // 初始化Express const app = express(); // 连接数据库 connectDB(); // 配置中间件 app.use(cors()); app.use(express.json()); app.use(express.urlencoded({ extended: true })); // 初始化Passport app.use(passport.initialize()); app.use(passport.session()); // 如果用session的话开启,REST API用JWT可以注释,但回调路由需要session暂存状态 // 挂载路由 app.use('/auth', require('./routes/auth')); app.use('/api', require('./routes/api')); // 启动服务器 const PORT = process.env.PORT || 5000; app.listen(PORT, () => { console.log(`服务器运行在端口${PORT}`); });
7. 创建认证路由
在routes/auth.js里写OAuth2的授权、回调和退出路由:
const express = require('express'); const router = express.Router(); const passport = require('../config/passport'); // 跳转到OAuth2提供商的授权页面 router.get('/oauth2', passport.authenticate('oauth2')); // 授权后的回调路由 router.get('/oauth2/callback', passport.authenticate('oauth2', { failureRedirect: '/auth/failure' }), (req, res) => { // 授权成功,返回JWT令牌给客户端(可以重定向到前端页面,把token作为参数传递) res.json({ success: true, token: req.user.token, user: { id: req.user.user._id, email: req.user.user.email, displayName: req.user.user.displayName } }); } ); // 授权失败的路由 router.get('/failure', (req, res) => { res.json({ success: false, message: '认证失败' }); }); // 退出登录 router.get('/logout', (req, res) => { req.logout((err) => { if (err) return res.status(500).json({ success: false, message: '退出失败' }); res.json({ success: true, message: '已退出登录' }); }); }); module.exports = router;
8. 创建受保护的API路由
在routes/api.js里写需要认证才能访问的接口,用Passport中间件保护:
const express = require('express'); const router = express.Router(); const passport = require('../config/passport'); // 受保护的用户信息接口 router.get('/profile', passport.authenticate('oauth2', { session: false }), (req, res) => { res.json({ success: true, user: { id: req.user._id, email: req.user.email, displayName: req.user.displayName, provider: req.user.provider } }); }); module.exports = router;
9. 配置环境变量
在.env文件里填写你的配置信息,记得替换成自己的实际值:
PORT=5000 MONGODB_URI=mongodb://localhost:27017/oauth2-auth-db # OAuth2提供商配置(以GitHub为例) OAUTH2_PROVIDER=github OAUTH2_AUTH_URL=https://github.com/login/oauth/authorize OAUTH2_TOKEN_URL=https://github.com/login/oauth/access_token OAUTH2_CLIENT_ID=你的GitHub客户端ID OAUTH2_CLIENT_SECRET=你的GitHub客户端密钥 OAUTH2_CALLBACK_URL=http://localhost:5000/auth/oauth2/callback JWT_SECRET=你的JWT密钥(随便写个长字符串)
10. 测试认证流程
- 启动开发服务器:
npm run dev - 打开浏览器访问
http://localhost:5000/auth/oauth2,会跳转到OAuth2提供商的授权页面 - 授权成功后,会返回包含JWT令牌的JSON响应
- 用Postman或者curl访问
http://localhost:5000/api/profile,在请求头里添加Authorization: Bearer <你的JWT令牌>,就能获取到用户信息了
一些重要提醒
- 一定要在OAuth2提供商的后台(比如GitHub Developer Settings)配置好回调地址,必须和
OAUTH2_CALLBACK_URL完全一致 - 如果是生产环境,记得把
NODE_ENV设为production,并且不要把.env文件提交到版本库 - 如果不需要session,可以注释掉
app.use(passport.session()),但回调路由可能需要调整状态存储方式 - 可以根据需要扩展用户模型,添加更多字段比如头像地址等
内容的提问来源于stack exchange,提问作者Rajeev Varshney
相关产品推荐
相关产品推荐

