【问题标题】:Node.js - JWT, API: How To Implement Multiple Passport.js Authentication Middleware?Node.js - JWT、API:如何实现多个 Passport.js 身份验证中间件?
【发布时间】: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)时,它都会随机选择一个要执行的护照文件,而不考虑拨打电话的用户类型

标签: node.js express jwt passport.js middleware


【解决方案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 相同的操作?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2015-01-21
  • 2018-04-19
  • 2019-08-31
  • 2018-10-27
  • 2015-05-31
  • 2023-04-05
  • 1970-01-01
相关资源
最近更新 更多