新聞中心
安全規(guī)則是一個可以靈活地自定義數(shù)據(jù)庫和云存儲讀寫權限的權限控制方式,通過配置安全規(guī)則,開發(fā)者可以在小程序端、網(wǎng)頁端精細化的控制云存儲和集合中所有記錄的增、刪、改、查權限,自動拒絕不符合安全規(guī)則的前端數(shù)據(jù)庫與云存儲請求,保障數(shù)據(jù)和文件安全。

創(chuàng)新互聯(lián)建站2013年開創(chuàng)至今,先為漾濞等服務建站,漾濞等地企業(yè),進行企業(yè)商務咨詢服務。為漾濞企業(yè)網(wǎng)站制作PC+手機+微官網(wǎng)三網(wǎng)同步一站式服務解決您的所有建站問題。
一、{openid} 變量
在前面我們建議使用安全規(guī)則取代簡易版的權限設置,當使用安全規(guī)則之后,這里有一個重要的核心就是 {openid} 變量 ,無論在前端(小程序端、web端)查詢時,它都是必不可少的(也就是說云函數(shù),云開發(fā)控制臺不受安全規(guī)則控制)。
1、查詢寫入都需明確指定 openid
{openid} 變量在小程序端使用時無需先通過云函數(shù)獲取用戶的 openid,直接使用'{openid}'即可,而我們在查詢時都需要顯式傳入openid。之前我們使用簡易權限配置時不需要這么做,這是因為查詢時會默認給查詢條件加上一條 _openid 必須等于用戶 openid,但是使用安全規(guī)則之后,就沒有這個默認的查詢條件了。
比如我們在查詢collection時,都需要在where里面添加如下如下的條件,{openid}變量就會附帶當前用戶的openid。
db.collection('china').where({
_openid: '{openid}', //安全規(guī)則里有auth.openid時都需要添加
})
更新、刪除等數(shù)據(jù)庫的寫入請求也都需要明確在where里添加這樣的一個條件(使用安全規(guī)則后,在小程序端也可以進行批量更新和刪除)。
db.collection('goods').where({
_openid: '{openid}',
category: 'mobile'
}).update({ //批量更新
data:{
price: _.inc(1)
}
})開啟安全規(guī)則之后,都需要在where查詢條件里指定
_openid: '{openid}',這是因為大多數(shù)安全規(guī)則里都有auth.openid,也就是對用戶的身份有要求,where查詢條件為安全規(guī)則的子集,所以都需要添加。當然你也可以根據(jù)你的情況,安全規(guī)則不要求用戶的身份,也就可以不傳入_openid: '{openid}'了。
2、doc 操作需轉為 where 操作
由于我們在進行執(zhí)行doc操作db.collection('china').doc(id)時,沒法傳入openid的這個條件,那應該怎么控制權限呢?這時候,我們可以把doc操作都轉化為where操作就可以了,在where查詢里指定 _id 的值,這樣就只會查詢到一條記錄了:
db.collection('china').where({
_id: 'tcb20200501', //條件里面加_id
_openid: '{openid}', //安全規(guī)則里有auth.openid時都需要添加
})至于其他的doc操作,都需要轉化為基于collection的where操作,也就是說以后不再使用doc操作db.collection('china').doc(id)了。其中doc.update、doc.get和doc.remove可以用基于collection的update、get、remove取代,doc.set可以被更新指令_.set取代。當然安全規(guī)則只適用于前端(小程序端或Web端),后端不受安全規(guī)則的權限限制。
3、嵌套數(shù)組對象里的openid
在使用簡易權限配置時,用戶在小程序端往數(shù)據(jù)庫里寫入數(shù)據(jù)時,都會給記錄doc里添加一個_openid的字段來記錄用戶的openid,使用安全規(guī)則之后同樣也是如此。在創(chuàng)建記錄時,可以把{openid}變量賦值給非_openid的字段或者寫入到嵌套數(shù)組里,后臺寫入記錄時發(fā)現(xiàn)該字符串時會自動替換為小程序用戶的 openid:
db.collection('posts').add({
data:{
books:[{
title:"云開發(fā)快速入門",
author:'{openid}'
},{
title:"數(shù)據(jù)庫入門與實戰(zhàn)",
author:'{openid}'
}]
}
})
以往要進行openid的寫入操作時需要先通過云函數(shù)返回用戶openid,使用安全規(guī)則之后,直接使用{openid}變量即可,不過該方法僅支持add添加一條記錄時,不支持update的方式。
二、安全規(guī)則的寫法
使用安全規(guī)則之后,我們可以在控制臺(開發(fā)者工具和網(wǎng)頁)對每個集合以及云存儲的文件夾分別配置安全規(guī)則,也就是自定義權限,配置的格式是json,仍然嚴格遵循json配置文件的寫法(比如數(shù)組最后一項不能有逗號,,配置文件里不能有注釋等)。
1、粒度更細的增刪改查
我們先來看簡易權限配置所有用戶可讀,僅創(chuàng)建者可寫、僅創(chuàng)建者可讀寫、所有用戶可讀、所有用戶不可讀寫所對應的安全規(guī)則的寫法,這個json配置文件的key表示操作類型,value是一個表達式,也是一個條件,解析為true時表示相應的操作符合安全規(guī)則。
// 所有人可讀,僅創(chuàng)建者可讀寫
{
"read": true,
"write": "doc._openid == auth.openid"
}
//僅創(chuàng)建者可讀寫
{
"read": "doc._openid == auth.openid",
"write": "doc._openid == auth.openid"
}
//所有人可讀
{
"read": true,
"write": false
}
//所有用戶不可讀寫
{
"read": false,
"write": false
}簡易的權限配置只有讀read與寫write,而使用安全規(guī)則之后,支持權限操作有除了讀與寫外,還將寫權限細分為create新建、update更新、delete刪除,也就是既可以只使用寫,也可以細分為增、刪、改,比如下面的案例為 所有人可讀,創(chuàng)建者可寫可更新,但是不能刪除
"read": true,
"create":"auth.openid == doc._openid",
"update":"auth.openid == doc._openid",
"delete":false 操作類型無外乎增刪改查,不過安全規(guī)則的value是條件表達式,寫法很多,讓安全規(guī)則也就更加靈活。值得一提的是,如果我們不給read或者write賦值,它們的默認值為false。
2、所有用戶可讀可寫的應用
安全規(guī)則還可以配置所有人可讀可寫的類型,也就是如下的寫法,讓所有登錄用戶(用戶登錄了之后才有openid,即openid不為空)可以對數(shù)據(jù)可讀可寫。
{
"read": "auth.openid != null",
"write": "auth.openid != null"
}
在小程序端,我們可以把數(shù)據(jù)庫集合的安全規(guī)則操作read和write都寫為true(這是所有人可讀可寫,而這里強調的是所有用戶),因為只要用戶使用開啟了云開發(fā)的小程序,就會免鑒權登錄有了openid,但是上面安全規(guī)則的寫法則通用于云存儲、網(wǎng)頁端的安全規(guī)則。
集合里的數(shù)據(jù)讓所有用戶可讀可寫在很多方面都有應用,尤其是我們希望有其他用戶可以對嵌套數(shù)組和嵌套對象里的字段進行更新時。比如集合posts存儲的是所有資訊文章,而我們會把文章的評論嵌套在集合里。
{
_id:"tcb20200503112",
_openid:"用戶A", //用戶A也是作者,他發(fā)表的文章
title:"云開發(fā)安全規(guī)則的使用經(jīng)驗總結",
stars:223,
comments:[{
_openid:"用戶B",
comment:"好文章,作者有心了",
}]
}
當用戶A發(fā)表文章時,也就會創(chuàng)建這條記錄,如果用戶B希望可以評論(往數(shù)組comments里更新數(shù)據(jù))、點贊文章(使用inc原子更新更新stars的值),就需要對該記錄可讀可寫(至少是可以更新)。這在簡易權限配置是無法做到的(只能使用云函數(shù)來操作),有了安全規(guī)則之后,一條記錄就可以有被多個人同時維護的權限,而這樣的場景在云開發(fā)這種文檔型數(shù)據(jù)庫里比較常見(因為涉及到嵌套數(shù)組嵌套對象)。
安全規(guī)則與查詢where里的條件是相互配合的,但是兩者之間又有一定的區(qū)別。所有安全規(guī)則的語句指向的都是符合條件的文檔記錄,而不是集合。使用了安全規(guī)則的where查詢會先對文檔進行安全規(guī)則的匹配,比如小程序端使用where查詢不到記錄,就會報錯
errCode: -502003 database permission denied | errMsg: Permission denied,然后再進行條件匹配,比如安全規(guī)則設置為所有人可讀時,當沒有符合條件的結果時,會顯示查詢的結果為0。我們要注意無權查詢和查詢結果為0的區(qū)別。
3、全局變量
要搞清楚安全規(guī)則寫法的意思,我們還需要了解一些全局變量,比如前面提及的auth.openid表示的是登錄用戶的openid,而doc._openid表示的是當前記錄_openid這個字段的值,當用戶的openid與當前記錄的_openid值相同時,就對該記錄有權限。全局變量還有now(當前時間戳)和resource(云存儲相關)。
| 變量 | 類型 | 說明 |
|---|---|---|
| auth | object | 用戶登錄信息,auth.openid 也就是用戶的openid,如果是在web端它還有l(wèi)oginType登錄方式、uid等值 |
| doc | object | 表示當前記錄的內容,用于匹配記錄內容/查詢條件 |
| now | number | 當前時間的時間戳,也就是以從計時原點開始計算的毫秒 |
| resource | object | resource.openid為云存儲文件私有歸屬標識,標記所有者的openid |
4、運算符
安全規(guī)則的表達式還支持運算符,比如等于==,不等于!=,大于>,大于等于>=,小于<,小于等于<=,與&&,或||等等,后面會有具體的介紹。
| 運算符 | 說明 | 示例 | |
|---|---|---|---|
| == | 等于 | auth.openid == 'zzz' | 用戶的 openid 為 zzz |
| != | 不等于 | auth.openid != 'zzz' | 用戶的 openid 不為 zzz |
| > | 大于 | doc.age>10 | 查詢條件的 age 屬性大于 10 |
| >= | 大于等于 | doc.age>=10 | 查詢條件的 age 屬性大于等于 10 |
| < | 小于 | doc.age<10 | 查詢條件的 age 屬性小于 10 |
| <= | 小于等于 | doc.age<=10 | 查詢條件的 age 屬性小于等于 10 |
| in | 存在在集合中 | auth.openid in ['zzz','aaa'] | 用戶的 openid 是['zzz','aaa']中的一個 |
| !(xx in []) | 不存在在集合中,使用 in 的方式描述 !(a in [1,2,3]) |
!(auth.openid in ['zzz','aaa']) | 用戶的 openid 不是['zzz','aaa']中的任何一個 |
| && | 與 | auth.openid == 'zzz' && doc.age>10 | 用戶的 openid 為 zzz 并且查詢條件的 age 屬性大于 10 |
| || | 或 | auth.openid == 'zzz'|| doc.age>10 | 用戶的 openid 為 zzz 或者查詢條件的 age 屬性大于 10 |
| . | 對象元素訪問符 | auth.openid | 用戶的 openid |
| [] | 數(shù)組訪問符屬性 | doc.favorites[0] == 'zzz' | 查詢條件的 favorites 數(shù)組字段的第一項的值等于 zzz |
四、身份驗證
全局變量auth與doc的組合使用可以讓登錄用戶的權限依賴于記錄的某個字段,auth表示的是登錄用戶,而doc、resource則是云開發(fā)環(huán)境的資源相關,使用安全規(guī)則之后用戶與數(shù)據(jù)庫、云存儲之間就有了聯(lián)系。resource只有resource.openid,而doc不只有_openid,還可以有很多個字段,也就讓數(shù)據(jù)庫的權限有了很大的靈活性,后面我們更多的是以doc全局變量為例。
1、記錄的創(chuàng)建者
auth.openid是當前的登錄用戶,而記錄doc里的openid則可以讓該記錄與登錄用戶之間有緊密的聯(lián)系,或者可以說讓該記錄有了一個身份的驗證。一般來說doc._openid所表示的是該記錄的創(chuàng)建者的openid,簡易權限控制比較的也是當前登錄用戶是否是該記錄的創(chuàng)建者(或者為更加開放且粗放的權限)。
//登錄用戶為記錄的創(chuàng)建者時,才有權限讀
"read": "auth.openid == doc._openid",
//不允許記錄的創(chuàng)建者刪除記錄(只允許其他人刪除)
"delete": "auth.openid != doc._openid",
安全規(guī)則和where查詢是配套使用的,如果你指定記錄的權限與創(chuàng)建者的openid有關,你在前端的查詢條件的范圍就不能比安全規(guī)則的大(如果查詢條件的范圍比安全規(guī)則的范圍大就會出現(xiàn)database permission denied:
db.collection('集合id').where({
_openid:'{openid}' //有doc._openid,因此查詢條件里就需要有_openid這個條件,
key:"value"
})
.get().then(res=>{
console.log(res)
})
2、指定記錄的角色
1、把權限指定給某個人
安全規(guī)則的身份驗證則不會局限于記錄的創(chuàng)建者,登錄用戶的權限還可以依賴記錄的其他字段,我們還可以給記錄的權限指定為某一個人(非記錄的創(chuàng)建者),比如很多個學生提交了作業(yè)之后,會交給某一個老師審閱批改,老師需要對該記錄有讀寫的權限,在處理時,可以在學生提交作業(yè)(創(chuàng)建記錄doc)時時可以指定teacher的openid,只讓這個老師可以批閱,下面是文檔的結構和安全規(guī)則示例:
//文檔的結構
{
_id:"handwork20201020",
_openid:"學生的openid", //學生為記錄的創(chuàng)建者,
teacher:"老師的openid" //該學生被指定的老師的openid
}
//安全規(guī)則
{
"read": "doc.teacher == auth.openid || doc._openid == auth.openid",
"write": "doc.teacher == auth.openid || doc._openid == auth.openid",
}
讓登錄用戶auth.openid依賴記錄的其他字段,在功能表現(xiàn)上相當于給該記錄指定了一個角色,如直屬老師、批閱者、直接上級、閨蜜、夫妻、任務的直接指派等角色。
對于查詢或更新操作,輸入的where查詢條件必須是安全規(guī)則的子集,比如你的安全規(guī)則如果是
doc.teacher == auth.openid,而你在where里沒有teacher:'{openid}'這樣的條件,就會出現(xiàn)權限報錯。
由于安全規(guī)則和where查詢需要配套使用,安全規(guī)則里有doc.teacher和doc._openid,在where里也就需要寫安全規(guī)則的子集條件,比如_openid:'{openid}'或teacher:'{openid}',由于這里老師也是用戶,我們可以傳入如下條件讓學生和老師共用一個數(shù)據(jù)庫請求:
const db = wx.cloud.database()
const _ = db.command
//一條記錄可以同時被創(chuàng)建者(學生)和被指定的角色(老師)讀取
db.collection('集合id').where(_.or([
{_openid:'{openid}' }, //與安全規(guī)則doc._openid == auth.openid對應
{teacher:'{openid}' } //與安全規(guī)則doc.teacher == auth.openid對應
]))
.get().then(res=>{
console.log(res)
})2、把權限指定給某些人
上面的這個角色指定是一對一、或多對一的指定,也可以是一對多的指定,可以使用in或!(xx in [])運算符。比如下面是可以給一個記錄指定多個角色(學生創(chuàng)建的記錄,多個老師有權讀寫):
//文檔的結構
{
_id:"handwork20201020",
_openid:"學生的openid", //學生為記錄的創(chuàng)建者,
teacher:["老師1的openid","老師2的openid","老師3的openid"]
}
//安全規(guī)則
{
"read": "auth.openid in doc.teacher || doc._openid == auth.openid",
"write": "auth.openid in doc.teacher || doc._openid == auth.openid",
}這里要再強調的是前端(小程序端)的where條件必須是安全規(guī)則權限的子集,比如我們在小程序端針對老師進行如下查詢('{openid}'不支持查詢指令,需要后端獲取)
db.collection('集合id').where({
_openid:'{openid}',
teacher:_.elemMatch(_.eq('老師的openid'))
}).get()
.then(res=>{
console.log(res)
})前面我們實現(xiàn)了將記錄的權限指定給某個人或某幾個人,那如何將記錄的權限指定給某類人呢?比如打車軟件為了數(shù)據(jù)的安全性會有司機、乘客、管理員、開發(fā)人員、運維人員、市場人員等,這都需要我們在數(shù)據(jù)庫里新建一個字段來存儲用戶的類型,比如
{role:3},用1、2、3、4等數(shù)字來標明,或者用{isManager:true}boolean類型來標明,這個新增的字段可以就在查詢的集合文檔里doc.role,或者是一個單獨的集合(也就是存儲權限的集合和要查詢的集合是分離的,這需要使用get函數(shù)跨集合查詢),后面會有具體介紹。
3、doc.auth與文檔的創(chuàng)建者
下面有一個例子可以加深我們對安全規(guī)則的理解,比如我們在記錄里指定文檔的auth為其他人的openid,并配上與之相應的安全規(guī)則,即使當前用戶實際上就是這個記錄的創(chuàng)建者,這個記錄有該創(chuàng)建者的_openid,他也沒有操作的權限。安全規(guī)則會對查詢條件進行評估,只要符合安全規(guī)則,查詢才會成功,違反安全規(guī)則,查詢就會失敗。
//文檔的結構,比如以下為一條記錄
{
_id:"handwork20201020",
_openid:"創(chuàng)建者的openid",
auth:"指定的auth的openid"
}
//安全規(guī)則
{
"權限操作": "auth.openid == doc.auth" //權限操作為read、write、update等
}
//前端查詢,不符合安全規(guī)則,即使是記錄的創(chuàng)建者也沒有權限
db.collection('集合id').where({
auth:'{openid}'
})四、安全規(guī)則常用場景
簡易版權限設置沒法在前端實現(xiàn)記錄跨用戶的寫權限(含update、create、delete),也就是說記錄只有創(chuàng)建者可寫。而文檔型數(shù)據(jù)庫一個記錄因為反范式化嵌套的原因可以承載的信息非常多,B用戶操作A用戶創(chuàng)建的記錄,尤其是使用更新指令update字段以及內嵌字段的值這樣的場景是非常常見的。除此之外,僅安全規(guī)則可以實現(xiàn)前端對記錄的批量更新和刪除。
比如我們可以把評論、收藏、點贊、轉發(fā)、閱讀量等信息內嵌到文章的集合里,以往我們在小程序端(只能通過云函數(shù))是沒法讓B用戶對A用戶創(chuàng)建的記錄進行操作,比如點贊、收藏、轉發(fā)時用更新指令inc更新次數(shù),比如沒法直接用更新指令將評論push寫入到記錄里:
{
_id:"post20200515001",
title:"云開發(fā)安全規(guī)則實戰(zhàn)",
star:221, //點贊數(shù)
comments:[{ //評論和子評論
content:"安全規(guī)則確實是非常好用",
nickName:"小明",
subcomment:[{
content:"我也這么覺得",
nickName:"小軍",
}]
}],
share:12, //轉發(fā)數(shù)
collect:15 //收藏數(shù)
readNum:2335 //閱讀量
}在開啟安全規(guī)則,我們就可以直接在前端讓B用戶修改A用戶創(chuàng)建的記錄,這樣用戶閱讀、點贊、評論、轉發(fā)、收藏文章等時,就可以直接使用更新指令對文章進行字段級別的更新。
"read":"auth.openid != null",
"update":"auth.openid != null"這個安全規(guī)則相比于所有人可讀,僅創(chuàng)建者可讀寫,開放了update的權限,小程序端也有l(wèi)imit 20的限制。而如果不使用安全規(guī)則,把這些放在云函數(shù)里進行處理不僅處理速度更慢,而且非常消耗云函數(shù)的資源。
db.collection('post').where({
_id:"post20200515001",
openid:'{openid}'
}).update({
data:{
//更新指令的應用
}
})
五、數(shù)據(jù)驗證doc的規(guī)則匹配
我們還可以把訪問權限的控制信息以字段的形式存儲在數(shù)據(jù)庫的集合文檔里,而安全規(guī)則可以根據(jù)文檔數(shù)據(jù)動態(tài)地允許或拒絕訪問,也就是說doc的規(guī)則匹配可以讓記錄的權限動態(tài)依賴于記錄的某一個字段的值。
doc規(guī)則匹配的安全規(guī)則針對的是整個集合,而且要求集合里的所有記錄都有相應的權限字段,而只有在權限字段滿足一定條件時,記錄才有權限被增刪改查,是一個將集合的權限范圍按照條件要求收窄的過程,where查詢時的條件不能比安全規(guī)則規(guī)定的范圍大(查詢條件為安全規(guī)則子集);配置了安全規(guī)則的集合里的記錄只有兩種狀態(tài),有權限和沒有權限。
這里仍然再強調的是使用where查詢時要求查詢條件是安全規(guī)則的子集,在進行where查詢前會先解析規(guī)則與查詢條件進行校驗,如果where條件不是安全規(guī)則的子集就會出現(xiàn)權限報錯,不能把安全規(guī)則看成是一個篩選條件,而是一個保護記錄數(shù)據(jù)安全的不可逾越的規(guī)則。
1、記錄的狀態(tài)權限
doc的規(guī)則匹配,特別適合每個記錄存在多個狀態(tài)或每個記錄都有一致的權限條件(要么全部是,要么全部否),而只有一個狀態(tài)或滿足條件才有權限被用戶增刪改查時的情形,比如文件審批生效(之前存在審批沒有生效的多個狀態(tài)),文章的發(fā)布狀態(tài)為pubic(之前為private或其他狀態(tài)),商品的上架(在上架前有多個狀態(tài)),文字圖片內容的安全檢測不違規(guī)(之前在進行后置校驗),消息是否撤回,文件是否刪除,由于每個記錄我們都需要標記權限,而只有符合條件的記錄才有被增刪改查的機會。
比如資訊文章的字段如下,每個記錄對應著一篇文章,而status則存儲著文章的多個狀態(tài),只有public時,文章才能被用戶查閱到,我們可以使用安全規(guī)則"read": "doc.status=='public'"。而對于軟刪除(文章假刪除),被刪除可以作為一個狀態(tài),但是文章還是在數(shù)據(jù)庫里。
{
_id:"post2020051314",
title:"云開發(fā)發(fā)布新能力,支持微信支付云調用",
status:"public"
},
{
_id:"post2020051312",
title:"云函數(shù)灰度能力上線",
status:"edit"
},
{
_id:"post2020051311",
title:"云開發(fā)安全規(guī)則深度研究",
status:"delete"
}
而在前端(小程序端)與之對應的數(shù)據(jù)庫查詢條件則必須為安全規(guī)則的子集,也就是說安全規(guī)則不能作為你查詢的過濾條件,安全規(guī)則會對查詢進行評估,如果查詢不符合安全規(guī)則設置的約束(非子集),那么前端的查詢請求沒有權限讀取文檔,而不是過濾文檔:
db.collection('集合id').where({
status:"public" //你不能不寫這個條件,而指望安全規(guī)則給你過濾
}).get()
.then(res=>{
console.log(res)
})2、記錄禁止為空
有時候我們需要對某些記錄有著非常嚴格的要求,禁止為空,如何為空一律不予被前端增刪改查,比如已經(jīng)上架的shop集合里的商品列表,有些核心數(shù)據(jù)如價格、利潤、庫存等就不能為空,給企業(yè)造成損失,相應的安全規(guī)則和查詢如下:
//安全規(guī)則
{
"權限操作": "doc.profit != null",
}
//權限操作,profit = 0.65就是安全規(guī)則的子集
db.collection('shop').where({
profit:_.eq(0.65)
})3、記錄的子集權限
安全規(guī)則記錄的字段值不僅限于一個狀態(tài)(字符串類型),還可以是可以運算的范圍值,如大于>,小于<、in等,比如商品的客單價都是100以上,管理員在后端(控制臺,云函數(shù)等)把原本190元的價格寫成了19,或者失誤把價格寫成了負數(shù),這種情況下我們對商品集合使用安全規(guī)則doc.price > 100,前端將失去所有價格低于100的商品的操作權限,包括查詢。
//安全規(guī)則
"操作權限":"doc.price > 100"
//相應的查詢
db.collection('shop').where({
price:_eq(125)
})安全規(guī)則的全局變量now表示的是當前時間的時間戳,這讓安全規(guī)則可以給權限的時間節(jié)點和權限的時效性設置一些規(guī)則,這里就不具體講述了。
五、全局函數(shù)get構建權限體系
全局函數(shù)get可以實現(xiàn)跨集合來限制權限。doc的權限匹配更多的是基于文檔性質的權限,也就是集合內所有文檔都有相同的字段,根據(jù)這個字段的值的不同來劃分權限。但是有時候我們希望實現(xiàn)多個用戶和多個用戶角色來管理集合的文檔,擁有不同的權限,如果把用戶和角色都寫進文檔的每個記錄里,就會非常難以管理。也就是說doc的權限匹配并不適合復雜的用戶管理文檔的權限體系。
我們可以把單個復雜的集合文檔(反范式化的設計)拆分成多個集合文檔(范式化設計),將用戶和角色從文檔里分離出來。比如博客有文章post集合,而user集合除了可以把用戶劃分為作者、編輯、投稿者這樣的用戶身份,還可以是管理員組,編輯組等。如果我們把記錄的權限賦予給的人員比較多或群組比較復雜,則需要把角色存儲在其獨立的集合中,而不是作為目標文檔中的一個字段,用全局函數(shù)get來實現(xiàn)跨集合的權限限制。
get 函數(shù)是全局函數(shù),可以跨集合來獲取指定的記錄,用于在安全規(guī)則中獲取跨集合的記錄來參與到安全規(guī)則的匹配中,get函數(shù)的參數(shù)格式是 database.集合名.記錄id。
比如我們可以給文章post集合設置如下安全規(guī)則,只有管理員才可以刪除記錄,而判斷用戶是否為管理員則需要跨集合用user集合里的字段值來判斷:
//user集合的結構
{
_id:"oUL-m5FuRmuVmxvbYOGuXbuEDsn8", //用戶的openid
isManager:true
}
//post集合的權限
{
"read": "true",
"delete": "get(`database.user.${auth.openid}`).isManager== true"
}
db.collection('post').where({
//相應的條件,并不受子集的限制
})
get函數(shù)還可以接收變量,值可以通過多種計算方式得到,例如使用字符串模版進行拼接,這是一個查詢的過程,如果相應的文檔里有記錄,則函數(shù)返回記錄的內容,否則返回空(注意反引號的寫法):
`(database.${doc.collction}.${doc._id})`get函數(shù)的限制條件
- 安全規(guī)則里的get函數(shù) 參數(shù)中存在的變量 doc 需要在 query 條件中以 == 或 in 方式出現(xiàn),若以 in 方式出現(xiàn),只允許 in 唯一值, 即 doc.shopId in array, array.length == 1
- 一個表達式最多可以有 3 個 get 函數(shù),最多可以訪問 3 個不同的文檔。
- get 函數(shù)的嵌套深度最多為 2, 即 get(get(path))。
讀操作觸發(fā)與配額消耗說明
get 函數(shù)的執(zhí)行會計入數(shù)據(jù)庫請求數(shù),同樣受數(shù)據(jù)庫配額限制。在未使用變量的情況下,每個 get 會產(chǎn)生一次讀操作,在使用變量時,對每個變量值會產(chǎn)生一次 get 讀操作。例如:
假設某集合 shop 上有如下規(guī)則:
{
"read": "auth.openid == get(`database.shop.${doc._id}`).owner",
"write": false
}在執(zhí)行如下查詢語句時會產(chǎn)生 5 次讀取。
db.collection('shop').where(_.or([{_id:1},{_id:2},{_id:3},{_id:4},{_id:5}])).get() 文章名稱:創(chuàng)新互聯(lián)小程序云教程:云開發(fā)安全規(guī)則
文章位置:http://m.fisionsoft.com.cn/article/djhjpee.html


咨詢
建站咨詢
