NodeJS 链接中间件导致Express请求永久挂起

2wnc66cl  于 2023-01-25  发布在  Node.js
关注(0)|答案(3)|浏览(237)

我将使用this question中的中间件链接示例。
我有一个路由app.put('/users/:id', isAuthenticated, (req, res) => {db.updateUser(req.params.id, req.body)},我正在尝试编写一个中间件函数,该函数验证URL中提供的ID是否与从请求中包含的JWT中检索到的ID匹配。
我已经有了一个函数isAuthenticated,它验证JWT并将res.locals.userId设置为检索到的UID;所以我想在这个新函数canModifyTarget中简单地使用它,但是由于某种原因,请求永远挂起:

// This function works fine

isAuthenticated: function(req, res, next) {
  let token;
  if (req.headers.authorization && req.headers.authorization.split(' ')[0] === 'Bearer') {
    token = req.headers.authorization.split(' ')[1];
    admin.auth().verifyIdToken(token).then((decodedToken) => {
      res.locals.userId = decodedToken.uid;
      return next();
    }).catch((error) => {
      return res.status(HttpStatus.UNAUTHORIZED).send();
    })
  }
}
// Switching out isAuthenticated for this in the route causes a permanent hang

canModifyTarget: function(req, res, next) {
  console.log('This is printed');
  return (req, res, next) => {
    console.log('This is NOT printed');
    isAuthenticated(req, res, () => {
      if (req.params.id === res.locals.userId) {
        return next();
      }
      return res.status(HttpStatus.FORBIDDEN).send();
    })
  }
}
23c0lvtd

23c0lvtd1#

中间件应该是回调函数,一旦完成就调用“next()”。你的第一个函数,在执行时,调用next()(最终,在你的承诺被解决之后)
第二个函数没有调用next(),它只是返回一个函数定义。
像这样定义

canModifyTarget: function(req, res, next) {
    isAuthenticated(req, res, () => {
      if (req.params.id === res.locals.userId) {
        return next();
      }
      return res.status(HttpStatus.FORBIDDEN).send();
    })
  }
}

如果isAuthenticated的第三个参数是回调,则它应该可以工作
此外,您应该在isAuthenticated函数中定义一个“else”case,否则它也会挂起(可能抛出异常或其他东西?)
如果需要引用它们,请将它们存储在变量中,而不是直接在模块中定义它们。

const isAuthenticated = function(req, res, next) {
// code here
}

const canModifyTarget: function(req, res, next) {
// code here
 }

module.exports = {
   isAuthenticated,
   canModifyTarget,
};
5gfr0r5j

5gfr0r5j2#

我认为更简单的方法是将canModifyTarget定义为另一个中间件,即:

function canModifyTarget(req, res, next) {
    console.log('This is NOT printed');
    if (req.params.id === res.locals.userId) {
     return next();
    }
    return res.status(HttpStatus.FORBIDDEN).send();
  }

然后在isAuthenticated中间件之后应用它:

app.put(
    '/users/:id',
    isAuthenticated,
    canModifyTarget,
   (req, res) => {db.updateUser(req.params.id, req.body)}
);

希望有帮助。

xesrikrc

xesrikrc3#

我正在编写一个解决方案,其中我需要统一两种auth中间件:基于密码和基于APIKEY的集成到一个中间件中:统一的OrgAuth中间件。
因此,基本上这将使我能够将unifiedOrgAuth中间件放在那些需要基于密码或基于apikey的授权的路由上。
关键是将next函数从伞形中间件传递到底层中间件,只需使用伞形中间件的next函数调用底层中间件即可:
统一认证中间件:

function unifiedOrgAuthMiddleware(
  path: string,
  perm: Permission
): express.RequestHandler {
  return async (req: RequestWithOrg, _res: Response, next: NextFunction) => {
    
    const cookies = req.cookies;
    if (cookies && cookies.Authorization) {
      (userAuthMiddleware(path, perm))(req, _res, next);
      return;
    }
    const apiKey = req.header('X-API-KEY');
    if (apiKey && apiKey.length > 0) {
      (apiAuthMiddleware(path, perm))(req, _res, next);
      return;
    }
    return next(new Error401Exception());
    // Make linter happy.
  };
}

下面是底层中间件:
基于密码的身份验证中间件:

function userAuthMiddleware(
  path: string,
  perm: Permission
): express.RequestHandler {
  return async (req, _res, next) => {
    try {
      const cookies = req.cookies;
      if (!(cookies && cookies.Authorization)) {
        next(new Error401Exception());
        // Make linter happy.
        return;
      }
      if (!validCookies(cookies)) {
        next(new Error401Exception());
        // Make linter happy.
        return;
      }
    } catch (error) {
      next(new Error401Exception());
      // Make linter happy.
      return;
    }
    next();
  };
}

基于API认证中间件:

function apiAuthMiddleware(
  path: string,
  perm: Permission
): express.RequestHandler {
  return async (req: RequestWithOrg, _res: Response, next: NextFunction) => {
    const apiKey = req.header('X-API-KEY');
    if (!apiKey) {
      next(new Error401Exception());
      // Make linter happy.
      return;
    }
    if (!validApiKey(apiKey)) {
       next(new Error401Exception());
       // Make linter happy.
       return;
    }
    next();
  };
}

相关问题