使用Express JS检查API请求授权的更好方法
我有一个超级冗余的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...
什么是更好的方法?
你应该有一个中间件来检查每一个进来的请求,例如:
// 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() } })
在这个例子中,成功或失败都允许用户通过路由,但是只有当用户login时才会填充req.person
。请记住,由于async
关键字,这是一个asynchronous函数。 它返回一个承诺,我们正在使用try / catch架构。 这使我们可以使用await
来停止执行,直到jwt.verify()
已经填充完成为止。 这样就可以使req.person
在每个路由中都可用。 在本例中, person
是您在创buildJWT时执行jwt.sign()
时指定的属性。 这里是一个例子来慢跑你的记忆:
jwt.sign({ person: 'some unique identifier' }, 'secret', { expiresIn: '1y' })
这样, req.person
只能通过解码有效的JWT来填充。 不要被const { person } = await jwt.verify(token, SECRET)
所迷惑。 这和写作一样:
const decodedJWT = await jwt.verify(token, SECRET) const person = decodedJWT.person
在每一个受保护的路线中,您都可以简单地使第一行代码如下所示:
// 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) } })
此示例演示了EJS View模板引擎:
- 请参阅: https : //www.npmjs.com/package/ejs
然后, 在你所有的路线之后,放上一个图示路线和一个error handling中间件:
// 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 })
error handling中间件需要最后一个,它有一个额外的第四个参数是err
,只有当你传递一个参数,即next('Error happened')
,它才包含next()
参数的值。
上面的代码对GraphQL没有任何改变。 要在GraphQL中处理authentication,只需检查一下:
// 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 } }))
必须在authentication中间件之后安装GraphQL端点。 login的用户将作为context.person
在每个parsing器中可用,或者如果请求是非法的,context.person将是虚假的。 我提到这是为了将来寻找其他人。