Node.js - JWT、API:如何实现多个 Passport.js 身份验证中间件?
Posted
技术标签:
【中文标题】Node.js - JWT、API:如何实现多个 Passport.js 身份验证中间件?【英文标题】:Node.js - JWT, API: How To Implement Multiple Passport.js Authentication Middleware? 【发布时间】:2020-03-23 13:57:47 【问题描述】:所以我正在尝试为一个应用构建两个 JWT 身份验证系统。一种供用户使用,一种供企业使用。身份验证使用护照(本地和 JWT)。我开始研究身份验证的用户方面,并且能够使其按我的意愿工作。我认为商业也是如此。我所做的只是简单地复制粘贴并更改一些变量。通常,业务端点应该可以工作,但不能。所以我试图调试发生了什么,但我仍在试图弄清楚它是什么。我的猜测是,当我在 app.js 中加载策略时,只会调用用户的护照策略。如果我在passport
和business-passport
之间置换位置,一切都会中断。
这是我的代码。
Passport Middleware For User passport.js
const passport = require('passport');
const JwtStrategy = require('passport-jwt').Strategy;
const ExtractJwt = require('passport-jwt');
const LocalStrategy = require('passport-local').Strategy;
const JWT_SECRET = require('./config');
const User = require('./models/user');
// const Business = require('./models/business');
passport.use(new JwtStrategy(
jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
secretOrKey: JWT_SECRET,
passReqToCallback: true
, async (req, payload, done) =>
console.log('payload', payload)
try
// Find the user specified in token
const user = await User.findById(payload.sub);
// Handle if User dont exist
if (!user)
return done(null, false);
// Otherwise, return the user
req.user = user;
done(null, user);
catch (error)
done(error, false);
));
// Local Strategy
passport.use(new LocalStrategy(
usernameField: 'email'
, async (email, password, done) =>
try
// Find the user given the email
const user = await User.findOne(email);
// If not, handle it
if (!user)
return done(null, false);
// check if password is correct
const isMatch = await user.isValidPassword(password);
// if not, handle it
if (!isMatch)
return done(null, false);
// Otherwise, return the user
done(null, user);
catch (error)
done(error, false);
));
商业护照中间件 passport-business.js
只需更改企业用户
用户控制器 controllers/user.js
const fs = require('fs');
const mongoose = require('mongoose');
const JWT = require('jsonwebtoken');
const JWT_SECRET = require('../config');
const User = require('../models/user');
signToken = user =>
// Respond with token
return JWT.sign(
iss: 'My Business, Inc.',
userType: 'users',
sub: user._id, /* You can also use newUser.id */
iat: new Date().getTime(), /* Current Time */
exp: new Date().setDate(new Date().getDate() + 1), /* Current Time + 1 day ahead */
, JWT_SECRET);
;
module.exports =
signup: async (req, res, next) =>
console.log('req.value.body', req.value.body);
const email, password = req.value.body;
// Check if User already exist by email address
const foundUser = await User.findOne(email);
if(foundUser)
return res.status(403).json(error: 'Email is already in use');
// Create a new User
const newUser = new User(email, password);
await newUser.save();
// Generate the token
const token = signToken(newUser);
// Respond with token
res.status(200).json(token);
,
signin: async (req, res, next) =>
// Generate token
const token = signToken(req.user);
res.status(200).json(token);
// console.log('signin');
,
;
我之所以拥有req.value...
是因为我使用了一个中间件来验证我的正文和网址参数。
业务控制器 controllers/business.js 。 一切都一样,只需将用户更改为业务。
我还应该指出,每当我使用 jwt.io 查看我的令牌签名是否有效时,它总是显示 invalid signature
,但一切都按预期在我的应用程序上运行。想知道为什么它说invalid signature
。
用户路由 routes/user.js 。请记住,对于商业也是如此。
const express = require('express');
// this router deals better with "try catch" situations"
const router = require('express-promise-router')();
const passport = require('passport');
const UserController = require('../controllers/user');
require('../passport');
const validateBody, schemas, validateParam = require('../helpers/route-helpers');
const StrategyLocal = passport.authenticate('local', session: false);
const StrategyJWT = passport.authenticate('jwt', session: false);
router.get('/', (req, res) =>
res.json(message: "Welcome to our User's API Endpoint!");
);
router.route('/signup')
// .get(UserController.index)
.post([validateBody(schemas.authSchema)], UserController.signup);
router.route('/signin')
// Makes sense to implement localstrat here because jWt will always work because the user was issued a token at signup
// So both solutions will work here as a strategy
.post(validateBody(schemas.authSchema), StrategyLocal, UserController.signin);
module.exports = router;
最后,我认为导致问题的文件...介绍...app.js
const express = require('express');
const logger = require('morgan');
const mongoose = require('mongoose');
const bodyParser = require('body-parser');
const passport = require('passport');
const path = require('path');
const cors = require('cors');
const dbName = require('./config');
mongoose.Promise = global.Promise;
mongoose.connect('mongodb://localhost/' + dbName,
useNewUrlParser:true,
useUnifiedTopology:true,
useCreateIndex: true,
useFindAndModify: false
);
const app = express();
// ???????????????? This is where the problem is happening in my opinion
require('./passport-business')
require('./passport')
const user = require('./routes/user');
const business = require('./routes/business');
// Middlewares
// set the static files location /public/img will be /img for users
app.use(logger('dev'));
app.use(cors());
app.use(bodyParser.urlencoded( extended: false));
app.use(bodyParser.json());
app.use((req, res, next) =>
res.header("Access-Control-Allow-Origin", "*");
res.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept, Authorization, noauth");
if (req.method === 'OPTIONS')
res.header("Access-Control-Allow-Methods", "PUT, POST, PATCH, DELETE, OPTIONS");
res.setHeader('Access-Control-Allow-Credentials', false);
return res.status(200).json();
next();
);
app.use('/api/users', user);
app.use('/api/businesses', business);
// Start the server
const port = app.get('port') || 2019;
app.listen(port, () => console.log(`Server is listening on port $port`));
如果我保持一切原样,所有用户端点都可以工作,注册适用于企业,但是当我尝试登录(企业)时得到 401。但如果我这样做......
require('./passport')
require('./passport-business')
我尝试登录(用户)时得到 401,而我尝试登录(企业)时得到 500。
有人可以告诉我我在这里做错了什么吗?也许是我在 app.js 中加载这些文件的顺序。也许是因为我正在为用户和企业创建两个单独的passport
文件。 我很想知道如何将这两者合二为一。 .
【问题讨论】:
将“类型”编码到 JWT 中,根据类型重用一个中间件和一些逻辑不是更有意义吗? 嘿,麦克斯。如果您可以用一点代码进行详细说明,那将很有帮助。但这是我的问题之一;除了我遇到的问题之外,如何将两个护照中间件封装到一个文件中。 会不会是我在同一台计算机和服务器上同时测试用户和业务,这可能会在会话中产生冲突?我不确定,但这令人沮丧,我仍然无法继续我的项目。我真的需要一些帮助。 似乎每当我调用某个端点(/api/users
或/api/businesses
)时,它都会随机选择一个要执行的护照文件,而不考虑拨打电话的用户类型
【参考方案1】:
所以在passport.js
你这样做:
try
// Find the user specified in token
const user = await User.findById(payload.sub);
// Handle if User dont exist
if (!user)
return done(null, false);
// Otherwise, return the user
req.user = user;
done(null, user);
catch (error)
done(error, false);
除了克隆文件之外,您难道不能得到 userType
您已经登录 JWT 并设置条件吗?
即:
try
// Find the user specified in token
let user = null;
if (payload.userType === 'user')
user = await User.findById(payload.sub);
if (payload.userType === 'business')
user = await Business.findById(payload.sub);
// Handle if User dont exist
if (!user)
return done(null, false);
// Otherwise, return the user
req.user = user;
done(null, user);
catch (error)
done(error, false);
如果你需要两个护照文件,你只需要一个
【讨论】:
嗨,麦克斯,感谢您的回答。您将如何处理passport.use(new LocalStrategy( usernameField: 'email' ,
?您如何在此处包含 payload
以执行与 JWT 相同的操作?以上是关于Node.js - JWT、API:如何实现多个 Passport.js 身份验证中间件?的主要内容,如果未能解决你的问题,请参考以下文章
使用 JWT (Node.js + mongoose) 在 REST API 中管理用户权限的最佳方法
如何在 Node.js 应用程序的客户端站点上处理 JWT 令牌?