帮助用户和群组确保数据访问的安全性
许多协作式应用都会允许用户根据一组权限读取和写入不同的数据。例如,在文档编辑应用中,用户可能希望允许其他一些用户对其文档执行读写操作,同时阻止不必要的访问。
解决方案:基于角色的访问权限控制
您可以利用 Cloud Firestore 的数据模型以及自定义的安全规则在您的应用中实现基于角色的访问权限控制。
假设您正在构建一款协作式撰文应用,用户可以按照以下安全要求在其中撰写“故事”和“评论”:
- 每个故事都有一名所有者,故事可共享给“撰写者”、“评论者”和“读者”。
- “读者”只能查看故事和评论,不能编辑任何内容。
- “评论者”除了拥有读者所拥有的全部访问权限之外,还可以为故事添加评论。
- “撰写者”除了拥有评论者所拥有的全部访问权限之外,还可以编辑故事内容。
- “所有者”可以编辑故事的任意部分,并且可以控制其他用户的访问权限。
数据结构
假设您的应用有一个 stories 集合,其中每个文档代表一个故事。每个故事还有一个 comments 子集合,其中每个文档都是对该故事的评论。
要跟踪访问角色,请添加一个 roles 字段,该字段是用户 ID 和角色的映射:
/stories/{storyid}
{
title: "A Great Story",
content: "Once upon a time ...",
roles: {
alice: "owner",
bob: "reader",
david: "writer",
jane: "commenter"
// ...
}
}
评论仅包含两个字段,留言者的用户 ID 和一些内容:
/stories/{storyid}/comments/{commentid}
{
user: "alice",
content: "I think this is a great story!"
}
规则
既然数据库中记录了用户角色,那么您需要编写安全规则来进行角色验证。这些规则假设应用使用 Firebase 身份验证,因此 request.auth.uid 变量是用户的 ID。
第 1 步:先创建基本的规则文件,其中包含针对故事和评论的空白规则:
service cloud.firestore {
match /databases/{database}/documents {
match /stories/{story} {
// TODO: Story rules go here...
match /comments/{comment} {
// TODO: Comment rules go here...
}
}
}
}
步骤 2 :添加一条简单的 write 规则,让所有者对故事拥有完全的控制权。所定义的函数可帮助确定某位用户的角色以及新文档是否有效:
service cloud.firestore {
match /databases/{database}/documents {
match /stories/{story} {
function isSignedIn() {
return request.auth != null;
}
function getRole(rsc) {
// Read from the "roles" map in the resource (rsc).
return rsc.data.roles[request.auth.uid];
}
function isOneOfRoles(rsc, array) {
// Determine if the user is one of an array of roles
return isSignedIn() && (getRole(rsc) in array);
}
function isValidNewStory() {
// Valid if story does not exist and the new story has the correct owner.
return resource == null && isOneOfRoles(request.resource, ['owner']);
}
// Owners can read, write, and delete stories
allow write: if isValidNewStory() || isOneOfRoles(resource, ['owner']);
match /comments/{comment} {
// ...
}
}
}
}
第 3 步:编写规则,以允许任何角色的用户阅读故事和发表评论。使用上一步所定义的函数使规则简明易懂:
service cloud.firestore {
match /databases/{database}/documents {
match /stories/{story} {
function isSignedIn() {
return request.auth != null;
}
function getRole(rsc) {
return rsc.data.roles[request.auth.uid];
}
function isOneOfRoles(rsc, array) {
return isSignedIn() && (getRole(rsc) in array);
}
function isValidNewStory() {
return resource == null
&& request.resource.data.roles[request.auth.uid] == 'owner';
}
allow write: if isValidNewStory() || isOneOfRoles(resource, ['owner']);
// Any role can read stories.
allow read: if isOneOfRoles(resource, ['owner', 'writer', 'commenter', 'reader']);
match /comments/{comment} {