使用Express JS检查API请求授权的更好方法

tom*_*tom 4 restful-authentication node.js express jwt graphql

我有一个超级冗余的server.js文件,因为几乎所有属于REST API的方法都以这种方式启动,因为我必须检查API请求,以确保客户端是否有权要求特定的东西。

var jwt = require('jsonwebtoken');

// ...

app.get('/getsomething', function(req, res) {
  "use strict";
  var token = req.headers[tokenName];
  if (token) {
    jwt.verify(token, app.get('some_secret'), {
      ignoreExpiration: false
    }, function(err, decoded) {
      if (err || typeof decoded === "undefined") {
        res.json({
          status: 401,
          message: "unauthorized"
        });
      }
      else { // actual code starts here...
Run Code Online (Sandbox Code Playgroud)

有什么更好的方法?

agm*_*984 5

您应该有一个中间件来检查每个传入的请求,如下所示:

// AUTHENTICATION
app.use(async (req) => {
    try {
        const token = req.headers.authorization
        const { person } = await jwt.verify(token, SECRET)
        req.person = person
        return req.next()
    } catch (e) {
        return req.next()
    }
})
Run Code Online (Sandbox Code Playgroud)

在此示例中,成功或失败都允许用户通过该路由,但是req.person只有在用户登录后才填充该路由。请记住,由于async关键字的缘故,这是一个异步函数。它返回了一个承诺,我们正在使用try / catch体系结构。这允许我们使用await暂停执行直到jwt.verify()填充执行person。这样一来req.person,每条路线都可以使用。在此示例中,personjwt.sign()在创建JWT 时指定的属性。这是一个慢跑记忆的例子:

jwt.sign({
  person: 'some unique identifier'
}, 'secret', { expiresIn: '1y' })
Run Code Online (Sandbox Code Playgroud)

这样,req.person只能通过解码有效的JWT来填充。不要被混淆const { person } = await jwt.verify(token, SECRET)。这与写作相同:

const decodedJWT = await jwt.verify(token, SECRET)
const person = decodedJWT.person
Run Code Online (Sandbox Code Playgroud)

在每条受保护的路由中,您都可以简单地使第一行代码如下所示:

// PROTECTED
app.get('/radical', async (req, res, next) => {
    try {
        // If req.person is falsy, the user is not logged in
        if (!req.person) return res.status(403).render('error/403')
        // Otherwise, the user is logged in, allow him/her to continue
        // Replace res.render() with res.json() in your case.
        return res.render('radical/template', {
            person: req.person
        })
    } catch (e) {
        return next(e)
    }
})
Run Code Online (Sandbox Code Playgroud)

此示例演示了EJS视图模板引擎:

然后,所有路由之后,放置一个splat路由和一个错误处理中间件:

// SPLAT ROUTE
app.get('*', (req, res, next) => {
    return res.status(404).render('error/404')
})

// ERRORS
app.use((err, req, res, next) => {
    res.status(500).render('error/500')
    throw err
})
Run Code Online (Sandbox Code Playgroud)

错误处理中间件需要排在最后,它还有一个附加的第4个参数,即仅当您将参数传递给它时,err它才包含参数的值next(),即:next('Error happened')

上面的代码也可以对GraphQL进行任何更改。要在GraphQL中处理身份验证,只需检查以下内容:

// GRAPHQL
app.use('/graphql', bodyParser.json(), graphqlExpress((req) => {
    const context = {
        person: req.person
    }
    return {
        schema,
        context,
        rootValue: null,
        formatError: (error) => ({
            message: error.message,
            locations: error.locations,
            stack: error.stack,
            path: error.path
        }),
        debug: true
    }
}))
Run Code Online (Sandbox Code Playgroud)

GraphQL端点必须在身份验证中间件之后安装。登录的用户将在每个解析器中以形式提供context.person,或者,如果请求是非法的,context.person将是虚假的。我为将来搜索的其他任何人提到了这一点。

  • 很高兴听到。这样可以很好地对待您,并且具有很好的可扩展性。我建议您现在也可以通读Express文档。您将学习各种小技巧。他们的文档很棒,组合得很好。关于中间件和错误处理的部分将向您展示我所说的一切。我建议您在那些文档中的各处阅读有关`next()`的内容,直到您对它的工作原理感到满意为止。它是向您展示中间件如何工作以及为什么我们可能关心高阶组件的理想人选。无关,但是任何异步/等待都值得研究。 (2认同)