我已经能够将此问题简化为一个单文件示例,该示例应该可以使用 Express 5.0.0-beta.1 轻松重现。
我正在编写一个 Express 中间件函数,它实现基本的 JWT 身份验证。逻辑非常简单:从
Authorization
标头中提取令牌(如果不存在/格式错误,则返回 401),然后将令牌插入到我们称为 validateToken
的内部函数中,该函数返回 Promise。 注意:我相信这是 Stack Overflow 上的一个独特问题,这个问题是关于从 在端点处理程序中进行异步调用,但我需要从 在中间件函数中进行异步调用。
我尝试通过以下方式实现这一点:
const express = require('express');
const app = express();
class HttpError {
constructor(status, message) {
this.status = status
this.message = message
}
}
// Auth middleware
const authMiddleware = (req, res, next) => {
const token = req.header('Authorization');
if (!token) {
console.info("No JWT in request")
return next(new HttpError(401, 'Unauthorized'))
}
const regex = /^Bearer (.+)$/;
const jwt = regex.exec(token)[1];
if (!jwt) {
console.info("Malformed JWT")
return next(new HttpError(401, 'Unauthorized'))
}
return validateToken({jwt: jwt, path: '/secured'})
.then((response) => next())
.catch(err => next(new HttpError(403, 'Forbidden')))
}
app.use(authMiddleware)
// Endpoint definitions
app.get('/secured', (req, res) => {
console.info('endpoint handler invoked')
res.send('Valid token')
})
// Error handlers
function errorMiddleware(error, req, res, next) {
console.info(`error middleware hit: ${JSON.stringify(error)}`)
if (!error.status || error.status >= 500) {
return res.status(error.status || 500).send();
}
res.status(error.status).json({ message: error.message });
}
app.use(errorMiddleware)
app.listen(8080);
当我发送不带
Authorization
标头的请求并且触发前两个 if
块之一(返回 next(new HttpError(...)
)时,应用程序立即向客户端发出 401 Unauthorized 响应。但是,当我发送带有标头中的令牌的请求时,应用程序会无限期地挂在中间件函数的最后 return validateToken(...)
行上。我被告知 Express 5.x 会自动处理中间件函数中被拒绝的 Promise,但即使升级到 Express 5 后,应用程序在调用异步函数时仍然会无限期挂起 validateToken
。
validateToken
函数有一些非常奇怪的行为 - 在验证令牌之前,它等待对 AWS 元数据服务的 HTTP 调用以获取有关系统的一些信息。我以为我的异步 Express 代码犯了一个错误,但实际上
对 AWS 的调用需要 75 秒才超时,超时后,处理程序完全按照应有的方式运行。 这是一个 Express 服务器的单文件示例,它等待异步操作在中间件中
,希望这对将来的人有所帮助:
const express = require('express');
const app = express();
class HttpError {
constructor(status, message) {
this.status = status
this.message = message
}
}
// Auth middleware
const authMiddleware = (req, res, next) => {
const token = req.header('Authorization');
if (!token) {
console.info("No JWT in request")
return next(new HttpError(401, 'Unauthorized'))
}
const regex = /^Bearer (.+)$/;
const jwt = regex.exec(token)[1];
if (!jwt) {
console.info("Malformed JWT")
return next(new HttpError(401, 'Unauthorized'))
}
// Assume that `validateToken` is a function returning a promise with either the token's claims or an error
return this.validateToken({jwt: jwt, path: '/secured'})
.then((response) => next())
.catch(err => next(new HttpError(403, 'Forbidden')))
}
// Invoke authMiddleware function for all endpoints that begin with '/secured'
app.use('/secured', authMiddleware)
// Endpoint definitions
app.get('/secured', (req, res) => {
res.send('Valid token')
})
app.get('/unsecured', (req, res) => res.send('Unsecured endpoint'))
// Error handlers
function errorMiddleware(error, req, res, next) {
console.debug(`Error caught by Express error handler: ${JSON.stringify(error)}`)
if (!error.status || error.status >= 500) {
return res.status(error.status || 500).send();
}
res.status(error.status).json({ message: error.message });
}
app.use(errorMiddleware)
app.listen(8080);