加密 Nodejs JWT 令牌

Posted

技术标签:

【中文标题】加密 Nodejs JWT 令牌【英文标题】:Encrypt Nodejs JWT Token 【发布时间】:2020-07-15 03:55:33 【问题描述】:

我一直在学习从article 创建测试 API 应用程序的教程。在文章的结尾,我看到一个提到最好加密 jwt 令牌以增加安全性,所以我也在寻找一种方法来做到这一点。我遇到了这个article,它提供了如何使用 RSA 私钥/公钥加密 jwt 令牌的示例。

这就是我卡住的地方。在我使用 /signup 路由成功注册后,我可以使用 /login 路由获取我的令牌。所以我假设这是我在将令牌发送回用户之前使用我的私钥加密令牌的地方?

**将repo公开用于测试 - 您只需在 app.js 中提供一个 mongoDB 连接字符串

我被困在该过程的加密/解密部分,感谢任何帮助。

router.post("/login", async (req, res, next) => 
  passport.authenticate("token", async (err, user, info) => 
    try 
      if (err || !user) 
        const error = new Error("An Error occurred");
        return next(error);
      
      req.login(user,  session: false , async error => 
        if (error) return next(error);
        //We don't want to store the sensitive information such as the
        //user password in the token so we pick only the email and id
        const body =  _id: user._id, email: user.email ;
        //Sign the JWT token and populate the payload with the user email and id
        const token = jwt.sign( user: body , PRIV_KEY,  algorithm: 'RS256' );
        //Send back the token to the user
        return res.json( token );
      );
     catch (error) 
      return next(error);
    
  )(req, res, next);
);

然后在调用“安全”路由时,我需要根据公钥解密令牌和verify

router.get("/profile", (req, res, next) => 
  //We'll just send back the user details and the token

  jwt.verify(req.query.token, PUB_KEY,  algorithms: ['RS256'] , function(err, decoded) 
    if (err.name === "TokenExpiredError") 
      console.log("Whoops, your token has expired!");
    

    if (err.name === "JsonWebTokenError") 
      console.log("That JWT is malformed!", err); <------ GET ERROR HERE
    

    if (err === null) 
      console.log("Your JWT was successfully validated!");
    

    // Both should be the same
    console.log(decoded);
    res.json(
      message: "You made it to the secure route",
      user: req.user
    );
  );
);

【问题讨论】:

【参考方案1】:

我没有时间重现这个。您的登录部分似乎正确。但是,您应该尝试像这样设置受保护的路由,从您的第一篇文章中复制并根据您的需要进行定制:

设置中间件以处理 jwt 解密,如果您在单独的文件中设置它,请确保在您的 app.js 或任何您需要的地方需要它。这可以稍后在您的控制器中用作中间件:

const JWTstrategy = require('passport-jwt').Strategy;
//We use this to extract the JWT sent by the user
const ExtractJWT = require('passport-jwt').ExtractJwt;

//This verifies that the token sent by the user is valid
passport.use(new JWTstrategy(
  //secret we used to sign our JWT
  secretOrKey : PUB_KEY,
  algorithms: ['HS256'],
  //we expect the user to send the token as a query parameter with the name 'token'
  jwtFromRequest : ExtractJWT.fromUrlQueryParameter('token')
, async (token, done) => 
  try 
    //Pass the user details to the next middleware
    return done(null, token.user);
   catch (error) 
    done(error);
  
));

设置保护路由,注意不需要手动调用jwt.verify,中间件处理并填充req.user

const express = require('express');

const router = express.Router();

//Let's say the route below is very sensitive and we want only authorized users to have access

//Displays information tailored according to the logged in user
router.get('/profile', passport.authenticate('jwt',  session: false ), (req, res, next) => 
  //We'll just send back the user details and the token
  res.json(
    message : 'You made it to the secure route',
    user : req.user,
    token : req.query.token
  )
);

module.exports = router;

**根据您的评论更新:

我克隆了你的 repo,它对我有用,虽然我改变了一些东西:

我添加了 app.use(bodyParser.json());app.js 以便我可以将请求正文作为 json 发送 - 如果您更喜欢 urlencoded,则不需要这样做

问题是您导出的secureRoute 是另一个路由器,您尝试将其用作app.js 中的控制器:

...
const secureRoute = require('./routes/secure-routes');
...
app.use('/user', passport.authenticate('jwt',  session: false ), secureRoute);`

*注意是/user路由,如果你想要/profile请改成app.use('/profile', ...)

所以而不是

router.get("/profile", (req, res, next) => 
  //We'll just send back the user details and the token
  res.json(
    message: "You made it to the secure route",
    user: req.user,
    token: req.query.secret_token
  );
);

它应该只是一个控制器功能:

...
module.exports = (req, res, next) => 
  //We'll just send back the user details and the token
  res.json(
    message: 'You made it to the secure route',
    user: req.user,
    token: req.query.token // note that you don't use secret_token but token as a name
  );
;
第三件事是不要忘记在查询参数中添加令牌,当您调用API时,例如http://localhost:3000/user?token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyIjp7Il9pZCI6IjVlODc2Yjc1YTVlNTk3MTRlOGFjMmI4NyIsImVtYWlsIjoiYUBiLmNvbSJ9LCJpYXQiOjE1ODU5MzMyNjR9.lcLuQeCMRy7Ef9zNkIt_rn4S22t2cm7YLRE7Jgp1Mpw

你应该得到回应:


    "message": "You made it to the secure route",
    "user": 
        "_id": "5e876b75a5e59714e8ac2b87",
        "email": "a@b.com"
    

【讨论】:

因此,当我尝试这样做时,一旦我获得令牌,然后将其应用于 /profile 路由,我就会从 Express 收到 401 Unauthorized 响应。 我也将公共仓库添加到我的 github 项目中。只需要将自己的 mongoDB 连接字符串添加到 app.js 刚刚根据您的 repo 快速复制。请检查编辑后的答案。 因此,在您的两个 cmets 中进行建议的更改后,当使用令牌请求 /profile 路由时,我仍然会收到 401 错误。我已经检查了对 github repo 的所有更改 我分叉了你的仓库:github.com/f4z3k4s/PassportTest。请检查,工作。所以基本上你还是有一些不一致的地方。您使用 publicKey 进行解密,但使用字符串进行加密。另一件事是你应该去 /user?token=xy 而不是 /profile?token=xy。第三件事是您需要使用私钥进行加密和解密。为什么?因为您的密钥文件不适合使用公钥解密。更多信息请见:***.com/questions/55012194/…

以上是关于加密 Nodejs JWT 令牌的主要内容,如果未能解决你的问题,请参考以下文章

如何验证nodejs中的jwt令牌/永不过期?

NodeJs - 从 JWT 令牌中检索用户信息?

Nodejs jwt 令牌永不过期

如何在 NodeJS 中使用 JWT 令牌验证路由

如何使用nodejs进行jwt令牌验证

加密 JWT 令牌