我正在尝试使用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
引用?有没有最好的做法?
此外,这是做我需要的正确方法还是已经有相同的官方/成熟图书馆?
答案 0 :(得分:2)
很好的no-sql数据库使您能够将子文档(或关系数据库中的等效表)嵌入到单个文档中。因此,您可以考虑将模式重新设计为类似
的模式{
userId:"",
categories": [
{
"categoryId": "",
"name": "",
"documents": [
{
"documentId": "",
},
{
"documentId": "",
},
]
},
{
"categoryId": "",
"name": "",
"documents": [
{
"documentId": "",
},
{
"documentId": "",
},
]
}
]
}
这可以帮助您优化数据库查询的数量,但这里需要注意的重要一点是,如果每个用户和每个类别的类别和文档数量分别增长很大,那么这种方法就不会很好。
永远记住mongo db架构设计的6个重要拇指规则
除非有令人信服的理由不
需要自行访问对象是不嵌入对象的一个令人信服的理由
数组不应无限增长。如果“多”方面有超过几百份文件,请不要嵌入;如果“many”方面有超过几千个文档,请不要使用ObjectID引用数组。高基数数组是不嵌入的一个令人信服的理由。
不要害怕应用程序级联接
考虑非规范化时的写入/读取比率。一个主要被读取且很少更新的字段是非规范化的良好候选。
您希望构建数据以匹配应用程序查询和更新数据的方式。
取自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;