1

我一直在关注这篇文章中关于创建测试 API 应用程序的教程。在文章的结尾,我看到一个提到最好加密 jwt 令牌以增加安全性,所以我也在寻找一种方法来做到这一点。我遇到了这篇文章,它提供了如何使用 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
    });
  });
});
4

1 回答 1

2

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

设置中间件来处理 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;

**根据您的评论更新:

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

  • 我添加 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"
    }
}
于 2020-04-03T14:47:07.743 回答