检查 Node.js REST API 上的资源所有权
Posted
技术标签:
【中文标题】检查 Node.js REST API 上的资源所有权【英文标题】:Check resource ownership on Node.js rest API 【发布时间】:2018-04-16 04:37:52 【问题描述】:我正在尝试使用 express 和 mongoose 开发 Node.js 后端。
网络上有很多关于如何实现适当的身份验证层的示例,但我找不到任何如何正确实现授权层的示例。
在我的具体情况下,我正在创建一个多用户应用程序的后端,我希望每个用户只能看到自己插入的数据。
我有三个模型:
-
用户
类别
文档
用户拥有一个或多个类别,类别包含零个或多个文档。
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
引用?有什么最佳做法吗?
此外,这是做我需要的正确方法还是有任何官方/成熟的库已经这样做了?
【问题讨论】:
这里有一个关系数据的例子。该解决方案在像 mongo 这样的 noSQL 数据库中并不优雅。我只想将“user_id”添加到文档中。这将防止将来出现优化问题。 @user1695032 谢谢,我认为最好通过user_id
检查所有权,否则我必须查询类别以查看所有者是否为user_id
并且仅在那个时候允许用户创建一个新文档,这会降低性能。对我来说似乎很奇怪,虽然这样的基本用例没有记录或得到图书馆的官方支持。
【参考方案1】:
no-sql 数据库使您能够将子文档(或关系数据库中的等效表)嵌入到单个文档中。因此,您可以考虑将架构重新设计为类似
userId:"",
categories": [
"categoryId": "",
"name": "",
"documents": [
"documentId": "",
,
"documentId": "",
,
]
,
"categoryId": "",
"name": "",
"documents": [
"documentId": "",
,
"documentId": "",
,
]
]
这可能会帮助您优化数据库查询的数量,但这里需要注意的重要一点是,如果每个用户和每个类别的类别和文档的数量分别增长得非常大,那么这种方法就不好了。
永远记住 mongo db 架构设计的 6 条重要经验法则
支持嵌入,除非有令人信服的理由不这样做
需要单独访问一个对象是不嵌入它的一个令人信服的理由
数组不应无限制地增长。如果“多”端有几百个文档,不要嵌入它们;如果“多”端有超过几千个文档,请不要使用 ObjectID 引用数组。高基数数组是不嵌入的一个令人信服的理由。
不要害怕应用级连接
在非规范化时考虑写入/读取比率。大部分会被读取但很少更新的字段是非规范化的良好候选者。
您希望构建数据以匹配应用程序查询和更新数据的方式。
取自here
【讨论】:
虽然这个答案对于决定如何设计模式非常有用,但我认为它并没有真正解决有关如何验证资源“所有权”的问题。它没有提供任何模式/最佳实践/库来实现此目的。【参考方案2】:经过一番修改,我最终得到了以下中间件。
它基本上按预期顺序检查路由参数并检查一致的成员资格。
不确定这是否是实现这一目标的最佳方式,但它确实有效:
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;
【讨论】:
该类别是否包含除名称和用户 ID 之外的其他信息? @pulankit 是的,实际代码在类别中存储了更多属性(例如“上次打开”、“处于活动状态”)。此外,我正在寻找一种适用于在每个通用深度 N 处嵌套的解决方案。以上是关于检查 Node.js REST API 上的资源所有权的主要内容,如果未能解决你的问题,请参考以下文章
如何在 MobileFirst Foundation 8 中使用 REST API 从 Node.js 发送推送通知?
nodejs使用Node.js实现REST Client调用REST API