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

时间:2017-11-03 12:31:20

标签: node.js rest express mongoose

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

在网络上有大量关于如何实施正确的身份验证图层的示例,但我找不到任何有关如何正确实施授权图层的示例。

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

我有三种模式:

  1. 用户
  2. 分类
  3. 文档
  4. 用户拥有一个或多个类别类别包含零个或多个文档

    CRUD操作在以下端点上实现:

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

    在身份验证部分,我设置为每个请求当前登录的用户ID,因此我可以轻松检查

    jsonwebtoken.userId == req.params.userid
    

    否则返回403错误。

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

    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引用?有没有最好的做法?

    此外,这是做我需要的正确方法还是已经有相同的官方/成熟图书馆?

2 个答案:

答案 0 :(得分:2)

很好的no-sql数据库使您能够将子文档(或关系数据库中的等效表)嵌入到单个文档中。因此,您可以考虑将模式重新设计为类似

的模式
{
  userId:"",
  categories": [
    {
      "categoryId": "",
      "name": "",
      "documents": [
        {
          "documentId": "",

        },
        {
          "documentId": "",

        },

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

        },
        {
          "documentId": "",

        },

      ]
    }
  ]
}

这可以帮助您优化数据库查询的数量,但这里需要注意的重要一点是,如果每个用户和每个类别的类别和文档数量分别增长很大,那么这种方法就不会很好。

永远记住mongo db架构设计的6个重要拇指规则

  1. 除非有令人信服的理由不

  2. ,否则请插入
  3. 需要自行访问对象是不嵌入对象的一个​​令人信服的理由

  4. 数组不应无限增长。如果“多”方面有超过几百份文件,请不要嵌入;如果“many”方面有超过几千个文档,请不要使用ObjectID引用数组。高基数数组是不嵌入的一个令人信服的理由。

  5. 不要害怕应用程序级联接

  6. 考虑非规范化时的写入/读取比率。一个主要被读取且很少更新的字段是非规范化的良好候选。

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

  8. 取自here

答案 1 :(得分:0)

经过一些修补,我最终得到了以下中间件。

它基本上按预期顺序检查路由参数,并检查连贯的成员资格。

不确定这是否是实现这一目标的最佳方法,但它确实有效:

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;