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 中加载策略时,只会调用用户的护照策略。如果我在passportbusiness-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 令牌?

在 Node.js 中使用 express-jwt 令牌保护非 api (res.render) 路由

快速中间件中的 Node.js JWT 刷新令牌

Node JS:只允许服务器端调用我的 api

如何保护简单的 Node.js RESTful API