检查Node.js rest API上的资源所有权

我正在尝试使用express和mongoose来开发一个Node.js后端。

在networking上,有很多关于如何实现一个合适的authentication层的例子,但是我找不到如何正确实现授权层的例子。

在我的具体情况下,我创build了一个多用户应用程序的后端,我希望每个用户只能看到自己插入的数据。

我有三个模型:

  1. 用户
  2. 类别
  3. 文件

用户拥有一个或多个类别类别包含零个或多个文档

CRUD操作在以下端点上执行:

/user/:userid /user/:userid/category /user/:userid/category/:categoryid /user/:userid/category/:categoryid/document /user/:userid/category/:categoryid/document/:documentid 

在authentication部分我设置为每个请求当前login的用户ID,所以我可以很容易地检查

 jsonwebtoken.userId == req.params.userid 

否则返回一个403错误。

检查类别的所有权非常容易,因为每个类别都包含对创build它们的用户的引用。

 var CategorySchema = mongoose.Schema({ name: { type: String, required: true, trim: true }, user_id: { type: mongoose.Schema.Types.ObjectId, ref: 'User', index: true } }); 

然而,在Document模型中,我只有一个对它所属的类别的引用,但是我没有给用户添加一个引用。

我想知道如何进行“嵌套”的关系。 我是否需要在任何深度级别添加一个user_id引用给它们全部? 有没有最佳做法?

而且,这是做我需要的正确的方法还是有任何官方/成熟的图书馆已经这样做?

那么没有SQL数据库给你的权力embedded你的子文件(或关系数据库中的等效表)在单个文件。 所以你可以考虑重新devise你的模式

 { userId:"", categories": [ { "categoryId": "", "name": "", "documents": [ { "documentId": "", }, { "documentId": "", }, ] }, { "categoryId": "", "name": "", "documents": [ { "documentId": "", }, { "documentId": "", }, ] } ] } 

这可能会帮助您优化分贝查询的数量,但是这里要注意的重要一点是,如果每个用户和每个类别的类别和文档数量可能会变得非常大,那么这种方法就不好。

永远记住mongo db模式devise的6个重要的拇指规则

  1. 除非有令人信服的理由,否则embeddedembedded

  2. 需要自行访问对象是一个不被embedded的理由

  3. arrays不应无限制地增长。 如果“多”一面有几百个文件,则不要embedded; 如果在“多”一侧有多于几千个文档,则不要使用ObjectID引用的数组。 高基数数组是一个不被embedded的令人信服的理由。

  4. 不要害怕应用程序级别的连接

  5. 考虑反规范化时的读写比率。 大部分将被读取并且只是很less更新的字段是非规范化的好候选者。

  6. 您希望构build数据以匹配应用程序查询和更新的方式。

从这里采取

经过一番修改之后,我结束了以下中间件。

它基本上以预期的顺序检查路线参数并检查一致的成员资格。

不知道这是否是实现这一目标的最佳方式,但它的工作原理是:

 var Category = require('../category/Category'), Document = require('../document/Document'), unauthorizedMessage = 'You are not authorized to perform this operation.', errorAuthorizationMessage = 'Something went wrong while validating authorizations.', notFoundMessage = ' not found.'; var isValidMongoId = function (id) { if (id.match(/^[0-9a-fA-F]{24}$/)) { return true; } return false; } var verifyPermissions = function (req, res, next) { if (req.userId) { if (req.params.userid && isValidMongoId(req.params.userid)) { if (req.userId != req.params.userid) { return res.status(403).send({error: 403, message: unauthorizedMessage}); } if (req.params.categoryid && isValidMongoId(req.params.userid)) { Category.findOne({_id: req.params.categoryid, user_id: req.params.userid}, function(err, category){ if (err) { return res.status(500).send({error: 500, message: errorAuthorizationMessage}) } if (!category) { return res.status(404).send({error: 404, message: 'Category' + notFoundMessage}); } if (req.params.documentid && isValidMongoId(req.params.documentid)) { Document.findOne({_id: req.params.documentid, category_id: req.params.categoryid}, function(err, document){ if (err) { return res.status(500).send({error: 500, message: errorAuthorizationMessage}) } if (!document) { return res.status(404).send({error: 404, message: 'Document' + notFoundMessage}); } }); } }); } } next(); } else { return res.status(403).send({error: 403, message: unauthorizedMessage}); } }; module.exports = verifyPermissions;