Node.js 如何在处理单用户帐户和多用户(具有角色的组织帐户)时创建MongoDB模式设计
我正在使用mongoDB和mongoose开发一个Nodejs Express API项目,我想从社区获得一些关于最佳实践的建议,并着手创建一个高效的模式设计 该应用程序处理两种类型的用户帐户 帐户类型:Node.js 如何在处理单用户帐户和多用户(具有角色的组织帐户)时创建MongoDB模式设计,node.js,mongodb,express,mongoose,mongoose-schema,Node.js,Mongodb,Express,Mongoose,Mongoose Schema,我正在使用mongoDB和mongoose开发一个Nodejs Express API项目,我想从社区获得一些关于最佳实践的建议,并着手创建一个高效的模式设计 该应用程序处理两种类型的用户帐户 帐户类型: 单个(默认) 组织(可以从设置切换到) 注意: 在组织帐户中,将有一个管理员(所有者)和其他受邀用户,并且每个用户都被分配了权限级别/访问级别。一个用户将始终仅与一个帐户关联,即如果他已经是现有帐户的一部分,则不能再次被邀请到另一个帐户或启动新帐户。此外,如果是组织帐户,则计费和发货地址特
- 单个(默认)
- 组织(可以从设置切换到)
const userSchema = new Schema({
first_name: String,
last_name: String,
email: String,
phone: String,
avatar: String,
password: String,
active: Boolean
});
const User = mongoose.model('user', userSchema);
const accountSchema = mongoose.Schema({
account_type: { type: String, enum: ['single', 'organization'], default: 'single' },
organization: { type: Schema.Types.ObjectId, ref: 'organization', required: false },
billing_address: String,
shipping_address: String,
});
const Account = mongoose.model('account', accountSchema);
const accountUserRoleSchema = mongoose.Schema({
user : { type: Schema.Types.ObjectId, ref: 'user', },
role: { type: String, enum: ['admin', 'user'], default: 'user' },
account: { type: Schema.Types.ObjectId, ref: 'account', required: true }
});
const AccountUserRole = mongoose.model('accountUserRole', accountUserRoleSchema);
const permissionSchema = mongoose.Schema({
user : { type: Schema.Types.ObjectId, ref: 'user', required: true },
type: { type: Schema.Types.ObjectId, ref: 'permissionType', required: true },
read: { type: Boolean, default: false, required: true },
write: { type: Boolean, default: false, required: true },
delete: { type: Boolean, default: false, required: true },
accountUser : { type: Schema.Types.ObjectId, ref: 'account',required: true }
});
const Permission = mongoose.model('permission', permissionSchema);
const permissionTypeSchema = mongoose.Schema({
name : { type: String, required: true }
});
const PermissionType = mongoose.model('permissionType', permissionTypeSchema);
const organizationSchema = mongoose.Schema({
account : { type: Schema.Types.ObjectId, ref: 'account', },
name: { type: String, required: true },
logo: { type: String, required: true }
});
const Organization = mongoose.model('organization', organizationSchema);
const userSchema = new Schema({
first_name: String,
last_name: String,
email: String,
phone: String,
avatar: String,
password: String,
active: Boolean
account : { type: Schema.Types.ObjectId, ref: 'account', },
role: { type: String, enum: ['admin', 'user'], default: 'user' },
permssion: [
{
type: { type: Schema.Types.ObjectId, ref: 'permissionType', required: true },
read: { type: Boolean, default: false, required: true },
write: { type: Boolean, default: false, required: true },
delete: { type: Boolean, default: false, required: true },
}
]
});
const User = mongoose.model('user', userSchema);
const accountSchema = mongoose.Schema({
account_type: { type: String, enum: ['single', 'organization'], default: 'single' },
organization: {
name: { type: String, required: true },
logo: { type: String, required: true }
},
billing_address: String,
shipping_address: String,
});
const Account = mongoose.model('account', accountSchema);
const permissionTypeSchema = mongoose.Schema({
name : { type: String, required: true }
});
const PermissionType = mongoose.model('permissionType', permissionTypeSchema);
现在我正在开发授权部分,其中需要通过检查分配给用户的权限来限制用户对资源的访问
我找到的解决方案是开发一个授权中间件,该中间件在验证中间件之后运行,该中间件检查分配的访问权限
但当我试图根据当前登录的用户访问帐户数据时出现了问题,因为我必须根据objectId引用搜索文档。我可以理解,如果我继续我目前的设计,这种情况可能会发生。这很好,但使用objectId引用搜索文档似乎不是一个好主意
授权中间件
module.exports = {
checkAccess : (permission_type,action) => {
return async (req, res, next) => {
// check if the user object is in the request after verifying jwt
if(req.user){
// find the accountUserRole with the user data from the req after passort jwt auth
const accountUser = await AccountUserRole.findOne({ user :new ObjectId( req.user._id) }).populate('account');
if(accountUser)
{
// find the account and check the type
if(accountUser.account)
{
if(accountUser.account.type === 'single')
{
// if account is single grant access
return next();
}
else if(accountUser.account.type === 'organization'){
// find the user permission
// check permission with permission type and see if action is true
// if true move to next middileware else throw access denied error
}
}
}
}
}
}
}
我决定放弃我目前的模式,因为我知道在NoSQL上强制使用RDBMS方法是个坏主意
与关系数据库不同,MongoDB的最佳模式设计在很大程度上取决于如何访问数据。您将如何使用帐户数据,以及如何访问帐户数据
我新重新设计的架构和模型
const userSchema = new Schema({
first_name: String,
last_name: String,
email: String,
phone: String,
avatar: String,
password: String,
active: Boolean
});
const User = mongoose.model('user', userSchema);
const accountSchema = mongoose.Schema({
account_type: { type: String, enum: ['single', 'organization'], default: 'single' },
organization: { type: Schema.Types.ObjectId, ref: 'organization', required: false },
billing_address: String,
shipping_address: String,
});
const Account = mongoose.model('account', accountSchema);
const accountUserRoleSchema = mongoose.Schema({
user : { type: Schema.Types.ObjectId, ref: 'user', },
role: { type: String, enum: ['admin', 'user'], default: 'user' },
account: { type: Schema.Types.ObjectId, ref: 'account', required: true }
});
const AccountUserRole = mongoose.model('accountUserRole', accountUserRoleSchema);
const permissionSchema = mongoose.Schema({
user : { type: Schema.Types.ObjectId, ref: 'user', required: true },
type: { type: Schema.Types.ObjectId, ref: 'permissionType', required: true },
read: { type: Boolean, default: false, required: true },
write: { type: Boolean, default: false, required: true },
delete: { type: Boolean, default: false, required: true },
accountUser : { type: Schema.Types.ObjectId, ref: 'account',required: true }
});
const Permission = mongoose.model('permission', permissionSchema);
const permissionTypeSchema = mongoose.Schema({
name : { type: String, required: true }
});
const PermissionType = mongoose.model('permissionType', permissionTypeSchema);
const organizationSchema = mongoose.Schema({
account : { type: Schema.Types.ObjectId, ref: 'account', },
name: { type: String, required: true },
logo: { type: String, required: true }
});
const Organization = mongoose.model('organization', organizationSchema);
const userSchema = new Schema({
first_name: String,
last_name: String,
email: String,
phone: String,
avatar: String,
password: String,
active: Boolean
account : { type: Schema.Types.ObjectId, ref: 'account', },
role: { type: String, enum: ['admin', 'user'], default: 'user' },
permssion: [
{
type: { type: Schema.Types.ObjectId, ref: 'permissionType', required: true },
read: { type: Boolean, default: false, required: true },
write: { type: Boolean, default: false, required: true },
delete: { type: Boolean, default: false, required: true },
}
]
});
const User = mongoose.model('user', userSchema);
const accountSchema = mongoose.Schema({
account_type: { type: String, enum: ['single', 'organization'], default: 'single' },
organization: {
name: { type: String, required: true },
logo: { type: String, required: true }
},
billing_address: String,
shipping_address: String,
});
const Account = mongoose.model('account', accountSchema);
const permissionTypeSchema = mongoose.Schema({
name : { type: String, required: true }
});
const PermissionType = mongoose.model('permissionType', permissionTypeSchema);
我仍然不确定这是否是正确的方法,请帮助我与您的建议 您可以合并用户和用户帐户架构: 添加了一些对您有用的文件
const userSchema = new Schema({
first_name: { type: String,default:'',required:true},
last_name: { type: String,default:'',required:true},
email: { type: String,unique:true,required:true,index: true},
email_verified :{type: Boolean,default:false},
email_verify_token:{type: String,default:null},
phone: { type: String,default:''},
phone_verified :{type: Boolean,default:false},
phone_otp_number:{type:Number,default:null},
phone_otp_expired_at:{ type: Date,default:null},
avatar: { type: String,default:''},
password: { type: String,required:true},
password_reset_token:{type: String,default:null},
reset_token_expired_at: { type: Date,default:null},
active: { type: Boolean,default:true}
account_type: { type: String, enum: ['single', 'organization'], default: 'single' },
organization: {type:Schema.Types.Mixed,default:{}},
billing_address: { type: String,default:''}
shipping_address: { type: String,default:''}
role: { type: String, enum: ['admin', 'user'], default: 'user' },
permission: [
{
type: { type: Schema.Types.ObjectId, ref: 'permissionType', required: true },
read: { type: Boolean, default: false, required: true },
write: { type: Boolean, default: false, required: true },
delete: { type: Boolean, default: false, required: true },
}
],
created_at: { type: Date, default: Date.now },
updated_at: { type: Date, default: Date.now }
});
在您的中间件中:
module.exports = {
checkAccess : (permission_type,action) => {
return async (req, res, next) => {
// check if the user object is in the request after verifying jwt
if(req.user){
if(req.user.account_type === 'single')
{
// if account is single grant access
return next();
}
else{
// find the user permission
// check permission with permission type and see if action is true
// if true move to next middileware else throw access denied error
}
}
}
}
};
我建议:
1-定义您的权限级别,例如:如果用户被分配到特定的角色/权限级别,则他可以访问哪些功能/选项
2-权限级别应通过数字(1=管理员,2=用户)等进行识别,并且该密钥应在MongoDB中编制索引(您也可以使用和依赖ObjectID)
3-您的用户对象/架构在Mongoose中应该只有一个类型为Number的权限键-无需为此创建单独的架构
const userSchema = new Schema({
first_name: String,
last_name: String,
email: String,
phone: String,
avatar: String,
password: String,
active: Boolean
account : { type: Schema.Types.ObjectId, ref: 'account', },
permssion: {type: Number, required: true, default: 2} // Default's User
});
使用这种方法,您可以修改auth-check中间件,只检查客户机发送的权限级别是否由数据库标识,如果由数据库标识,则向用户授予访问权限,否则抛出拒绝访问错误
如果需要,您可以添加另一个权限类型的字段,并返回权限名称,但我认为您应该在客户端处理它,而不是在服务器/be上
我部分理解了要求(不擅长阅读太多的单词),因此我保留了任何未触及的内容,让我知道。谢谢您的回答,但我需要保留特定于帐户的账单和发货地址,而不是特定于组织帐户的特定用户