Node.js 在本例中,如何使用Passport实现JWT

Node.js 在本例中,如何使用Passport实现JWT,node.js,authentication,jwt,passport.js,Node.js,Authentication,Jwt,Passport.js,我正在设置服务器,希望在用户登录后用JWT响应用户。我现在正在使用passport,但不知道在这种情况下如何实现JWT验证 以下是登录的路径: app.route('/login') .get(users.renderLogin) .post(users.auth); 当用户登录时,我会发送他的令牌和一些信息: exports.auth = function (req, res, next){ passport.authenticate('local', {sessi

我正在设置服务器,希望在用户登录后用JWT响应用户。我现在正在使用passport,但不知道在这种情况下如何实现JWT验证

以下是登录的路径:

  app.route('/login')
    .get(users.renderLogin)
    .post(users.auth);
当用户登录时,我会发送他的令牌和一些信息:

exports.auth = function (req, res, next){

  passport.authenticate('local', {session: false}, (err, user, info) => {

    if (err) { return next(err); }
      console.log(err);

    if (!user) { return res.redirect('/login'); }

    req.logIn(user, function(err) {
      if (err) { return next(err); }

      // Loged in 
      const userInfo = {
        username: user.username,
        name: user.name,
        age: user.age,
        groupid: user.groupid,
        email: user.email,
      }
      const token = jwt.sign(userInfo, process.env.JWT_SEC);
      res.json({
        user,
        token
      })
      // res.redirect('/');
    });
  })(req, res, next);
};
之后,我需要在每个用户调用中验证JWT令牌。有人有什么建议吗

谢谢

来自:

Passport的唯一目的是验证请求,它通过一组称为策略的可扩展插件来实现

此外,来自:

本地身份验证策略使用用户名和密码对用户进行身份验证。该策略需要一个verify回调,它接受这些凭证,并在提供用户的情况下完成调用

因此,passport的本地策略将只验证同时提供
用户名
密码
的请求。因此,客户端每次想要访问应用程序时都必须发送这些凭据

因此,要使用web令牌对请求进行身份验证,您需要提供一个设置JWT的登录过程。(然后,客户端只需发送令牌,而无需每次存储和传输清除密码)

为此,npm上有很多可用的软件包。我个人使用和推荐

用户模型 假设您有一个用户模型,例如

const UserSchema = new mongoose.Schema({
email: {
    type: String,
    unique: true,
    required: true,
    maxlength: 254,
    trim: true,
    match: /(?:[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*|"(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21\x23-\x5b\x5d-\x7f]|\\[\x01-\x09\x0b\x0c\x0e-\x7f])*")@(?:(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?|\[(?:(?:(2(5[0-5]|[0-4][0-9])|1[0-9][0-9]|[1-9]?[0-9]))\.){3}(?:(2(5[0-5]|[0-4][0-9])|1[0-9][0-9]|[1-9]?[0-9])|[a-z0-9-]*[a-z0-9]:(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21-\x5a\x53-\x7f]|\\[\x01-\x09\x0b\x0c\x0e-\x7f])+)\])/
    // Ref :            RFC 5322 compliant regex
    // Visualizer :     https://regexper.com/#(%3F%3A%5Ba-z0-9!%23%24%25%26'*%2B%2F%3D%3F%5E_%60%7B%7C%7D~-%5D%2B(%3F%3A%5C.%5Ba-z0-9!%23%24%25%26'*%2B%2F%3D%3F%5E_%60%7B%7C%7D~-%5D%2B)*%7C%22(%3F%3A%5B%5Cx01-%5Cx08%5Cx0b%5Cx0c%5Cx0e-%5Cx1f%5Cx21%5Cx23-%5Cx5b%5Cx5d-%5Cx7f%5D%7C%5C%5C%5B%5Cx01-%5Cx09%5Cx0b%5Cx0c%5Cx0e-%5Cx7f%5D)*%22)%40(%3F%3A(%3F%3A%5Ba-z0-9%5D(%3F%3A%5Ba-z0-9-%5D*%5Ba-z0-9%5D)%3F%5C.)%2B%5Ba-z0-9%5D(%3F%3A%5Ba-z0-9-%5D*%5Ba-z0-9%5D)%3F%7C%5C%5B(%3F%3A(%3F%3A(2(5%5B0-5%5D%7C%5B0-4%5D%5B0-9%5D)%7C1%5B0-9%5D%5B0-9%5D%7C%5B1-9%5D%3F%5B0-9%5D))%5C.)%7B3%7D(%3F%3A(2(5%5B0-5%5D%7C%5B0-4%5D%5B0-9%5D)%7C1%5B0-9%5D%5B0-9%5D%7C%5B1-9%5D%3F%5B0-9%5D)%7C%5Ba-z0-9-%5D*%5Ba-z0-9%5D%3A(%3F%3A%5B%5Cx01-%5Cx08%5Cx0b%5Cx0c%5Cx0e-%5Cx1f%5Cx21-%5Cx5a%5Cx53-%5Cx7f%5D%7C%5C%5C%5B%5Cx01-%5Cx09%5Cx0b%5Cx0c%5Cx0e-%5Cx7f%5D)%2B)%5C%5D)
    // SFSM :           https://en.wikipedia.org/wiki/Finite-state_machine
    // Xcrowzz' note :  Seems to work for 99.99% of email addresses, containing either local, DNS zone and/or IPv4/v6 addresses.
    // Xcrowzz' note :  Regex can be targeted for Catastrophic Backtracking (https://www.regular-expressions.info/catastrophic.html) ; See https://www.npmjs.com/package/vuln-regex-detector to check them before someone DDOS your app.
},
password: {
    type: String,
    required: true,
    minlength: 8,
    maxlength: 254,
    select: false
},
username: {
    type: String,
    required: false,
    unique: true,
    maxlength: 254
}
});
module.exports = mongoose.model('User', UserSchema);
用户控制器 现在,是时候实现JWT生成和签名了,这应该在用户成功登录时完成。Aka,当body请求中提供的
用户名
密码
与DB匹配时(不要忘记将清除密码与使用
bcrypt.compare
存储的哈希进行比较)

笔记
let token=jwt.sign({id:user.\u id},secret,{expiresIn:86400})
基于
id
和先前定义的
secret
创建令牌,该令牌应该是一个非常长的随机字符串,弱
secret
可能会破坏令牌的整体完整性和安全性

这是JWT最基本的配置,一些研究将证明此代码还没有准备好生产。但在你的情况下,这就足够了

身份验证控制器 最后,您必须设计身份验证中间件,将提供的令牌与之前签名的令牌进行比较。它再次使用
secret
来比较它们

const secret = (process.env.TokenSuperSecret) ? 
process.env.TokenSuperSecret : 'SuperSecret';
const jwt = require('jsonwebtoken');
const passport = require('passport');
const CustomStrategy = require('passport-custom');

passport.use('jwt', new CustomStrategy(async (req, callback, err) => {
const token = req.headers['x-access-token'];
if (!token) return callback(err);
await jwt.verify(token, secret, (err, decoded) => {
    if (err) return callback(err);
    req.userId = decoded.id;
    callback(null, req);
    });
}));

exports.isAuthenticated = passport.authenticate('jwt', {
  session: false
}, null);
笔记
jwt.verify(token,secret,(err,decoded)=>{}
返回一个承诺,其中包含
解码的用户ID
(您在登录过程中使用
jwt.sign
进行编码)。您可以随意将其传递给您的中间件,以了解当前正在执行请求的用户

再见,感谢所有的鱼!

来自:

Passport的唯一目的是验证请求,它通过一组称为策略的可扩展插件来实现

此外,来自:

本地身份验证策略使用用户名和密码对用户进行身份验证。该策略需要验证回调,该回调接受这些凭据,并在提供用户时完成调用

因此,passport的本地策略将只验证同时提供
用户名
密码
的请求。因此,客户端必须在每次访问应用程序时发送这些凭据

因此,要使用web令牌对请求进行身份验证,您需要提供一个设置JWT的登录过程。(然后,客户端只需发送令牌,而无需每次存储和传输清除密码)

为了做到这一点,npm上有很多可用的软件包。我个人使用并推荐

用户模型 假设您有一个用户模型,例如

const UserSchema = new mongoose.Schema({
email: {
    type: String,
    unique: true,
    required: true,
    maxlength: 254,
    trim: true,
    match: /(?:[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*|"(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21\x23-\x5b\x5d-\x7f]|\\[\x01-\x09\x0b\x0c\x0e-\x7f])*")@(?:(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?|\[(?:(?:(2(5[0-5]|[0-4][0-9])|1[0-9][0-9]|[1-9]?[0-9]))\.){3}(?:(2(5[0-5]|[0-4][0-9])|1[0-9][0-9]|[1-9]?[0-9])|[a-z0-9-]*[a-z0-9]:(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21-\x5a\x53-\x7f]|\\[\x01-\x09\x0b\x0c\x0e-\x7f])+)\])/
    // Ref :            RFC 5322 compliant regex
    // Visualizer :     https://regexper.com/#(%3F%3A%5Ba-z0-9!%23%24%25%26'*%2B%2F%3D%3F%5E_%60%7B%7C%7D~-%5D%2B(%3F%3A%5C.%5Ba-z0-9!%23%24%25%26'*%2B%2F%3D%3F%5E_%60%7B%7C%7D~-%5D%2B)*%7C%22(%3F%3A%5B%5Cx01-%5Cx08%5Cx0b%5Cx0c%5Cx0e-%5Cx1f%5Cx21%5Cx23-%5Cx5b%5Cx5d-%5Cx7f%5D%7C%5C%5C%5B%5Cx01-%5Cx09%5Cx0b%5Cx0c%5Cx0e-%5Cx7f%5D)*%22)%40(%3F%3A(%3F%3A%5Ba-z0-9%5D(%3F%3A%5Ba-z0-9-%5D*%5Ba-z0-9%5D)%3F%5C.)%2B%5Ba-z0-9%5D(%3F%3A%5Ba-z0-9-%5D*%5Ba-z0-9%5D)%3F%7C%5C%5B(%3F%3A(%3F%3A(2(5%5B0-5%5D%7C%5B0-4%5D%5B0-9%5D)%7C1%5B0-9%5D%5B0-9%5D%7C%5B1-9%5D%3F%5B0-9%5D))%5C.)%7B3%7D(%3F%3A(2(5%5B0-5%5D%7C%5B0-4%5D%5B0-9%5D)%7C1%5B0-9%5D%5B0-9%5D%7C%5B1-9%5D%3F%5B0-9%5D)%7C%5Ba-z0-9-%5D*%5Ba-z0-9%5D%3A(%3F%3A%5B%5Cx01-%5Cx08%5Cx0b%5Cx0c%5Cx0e-%5Cx1f%5Cx21-%5Cx5a%5Cx53-%5Cx7f%5D%7C%5C%5C%5B%5Cx01-%5Cx09%5Cx0b%5Cx0c%5Cx0e-%5Cx7f%5D)%2B)%5C%5D)
    // SFSM :           https://en.wikipedia.org/wiki/Finite-state_machine
    // Xcrowzz' note :  Seems to work for 99.99% of email addresses, containing either local, DNS zone and/or IPv4/v6 addresses.
    // Xcrowzz' note :  Regex can be targeted for Catastrophic Backtracking (https://www.regular-expressions.info/catastrophic.html) ; See https://www.npmjs.com/package/vuln-regex-detector to check them before someone DDOS your app.
},
password: {
    type: String,
    required: true,
    minlength: 8,
    maxlength: 254,
    select: false
},
username: {
    type: String,
    required: false,
    unique: true,
    maxlength: 254
}
});
module.exports = mongoose.model('User', UserSchema);
用户控制器 现在,是时候实现JWT生成和签名了,这应该在用户成功登录时完成。Aka,当body请求中提供的
用户名
密码
与DB匹配时(不要忘记将清除密码与使用
bcrypt.compare
存储的哈希进行比较)

笔记
let token=jwt.sign({id:user.\u id},secret,{expiresIn:86400})
基于
id
和先前定义的
secret
创建令牌,该令牌应该是一个非常长的随机字符串,弱的
secret
可能会破坏令牌的整个完整性和安全性

这是JWT最基本的配置,一些研究将证明此代码还没有准备好生产。但在您的情况下,这就可以了

身份验证控制器 最后,您必须设计身份验证中间件,将提供的令牌与之前签名的令牌进行比较。它再次使用
secret
对它们进行比较

const secret = (process.env.TokenSuperSecret) ? 
process.env.TokenSuperSecret : 'SuperSecret';
const jwt = require('jsonwebtoken');
const passport = require('passport');
const CustomStrategy = require('passport-custom');

passport.use('jwt', new CustomStrategy(async (req, callback, err) => {
const token = req.headers['x-access-token'];
if (!token) return callback(err);
await jwt.verify(token, secret, (err, decoded) => {
    if (err) return callback(err);
    req.userId = decoded.id;
    callback(null, req);
    });
}));

exports.isAuthenticated = passport.authenticate('jwt', {
  session: false
}, null);
笔记
jwt.verify(token,secret,(err,decoded)=>{}
返回一个承诺,其中包含
解码的用户ID
(您在登录过程中使用
jwt.sign
进行编码)。您可以随意将其传递给您的中间件,以了解当前正在执行请求的用户


再见,感谢所有的鱼!

我有一个使用bcrypt进行比较的模型,就像你建议的那样。我可以在这个用户模型中实现JWT吗?这是一个很好的实践?我已经更新了我的答案,以使用一个基本的用户模型。实际上,我真的不知道如何在我的案例中使用它。我将编辑我的问题,以更清楚地说明问题不是您没有像我在controllers/users.js中那样生成jwt@
let token=jwt.sign({id:user.\u id},secret,{expiresIn:86400})///24小时
。最后,您需要使用
jwt对该令牌进行解码。在自定义Passport策略@controllers/auth.js(或者我认为是Passport jwt包)中验证
这很有效!我使用了你建议的部分内容谢谢你的时间我有一个使用bcrypt进行比较的模型,就像你建议的那样。你可以吗