使用 Express JS 检查 API 请求授权的更好方法
A better way to check for authorization of API requests using Express JS
我有一个超级冗余的 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()
}
})
在此示例中,成功或失败都允许用户通过路由,但 req.person
仅在用户登录时才会填充。请记住,这是一个异步函数,因为async
关键字。它 returns 一个承诺,我们正在使用 try/catch 架构。这允许我们使用 await
暂停执行,直到 jwt.verify()
已填充 person
。这使得 req.person
在每条路线中都可用。在此示例中,person
是您在 jwt.sign()
创建 JWT 时指定的 属性。这是一个可以唤起您记忆的示例:
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 视图模板引擎:
然后,在你所有的路由之后,放置一个 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
})
错误处理中间件需要放在最后,它有一个额外的第 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
}
}))
必须在身份验证中间件之后 安装 GraphQL 端点。登录用户将在每个解析器中作为 context.person
可用,或者如果请求是非法的,context.person 将是假的。我会为以后搜索的任何其他人提及这一点。
我有一个超级冗余的 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()
}
})
在此示例中,成功或失败都允许用户通过路由,但 req.person
仅在用户登录时才会填充。请记住,这是一个异步函数,因为async
关键字。它 returns 一个承诺,我们正在使用 try/catch 架构。这允许我们使用 await
暂停执行,直到 jwt.verify()
已填充 person
。这使得 req.person
在每条路线中都可用。在此示例中,person
是您在 jwt.sign()
创建 JWT 时指定的 属性。这是一个可以唤起您记忆的示例:
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 视图模板引擎:
然后,在你所有的路由之后,放置一个 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
})
错误处理中间件需要放在最后,它有一个额外的第 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
}
}))
必须在身份验证中间件之后 安装 GraphQL 端点。登录用户将在每个解析器中作为 context.person
可用,或者如果请求是非法的,context.person 将是假的。我会为以后搜索的任何其他人提及这一点。