大家好,我是 Liberogic 的 Matsun。
上一篇文章介紹了透過 Supabase Auth 與 Cloudflare 打造的驗證型網頁應用程式。本次則要接續探討當時簡單提到的 **RLS(Row Level Security,列級安全性)**內容。
只要使用 Supabase Auth,
「是誰登入了系統」
就能確認,但在業務應用程式中,僅靠驗證並不足夠。
「這個人可以查看哪些資料」
「可以變更哪些資料」
都必須明確設定,而這正是 RLS 的用途。
本文將介紹在業務應用程式中如何使用 Supabase 的 RLS。
什麼是 RLS
RLS 是 Row Level Security 的縮寫,是一種以資料庫「列」為單位控制存取權限的機制。
例如,假設申請資料內容如下。
申請ID, 申請者, 所属会社, 所属拠点, 申請内容, ステータス
同一張表格中包含所有使用者的申請資料,
- 一般員工登入時,希望只顯示自己的申請。
- 主管登入時,希望顯示自己部門或據點的申請。
- 總部管理員登入時,希望顯示所有申請。
這類存取控制是在資料庫端執行的 RLS,也是業務應用程式中的重要功能之一。
Auth 與 RLS 的職責不同
如同上一篇文章所述,Supabase Auth 與 RLS 的職責不同。
將職責區分後,兩者的關係如下。
Supabase Auth → 誰なのか確認する
RLS → その人がどのデータにアクセスできるか決める
舉例來說,假設森本先生登入系統。透過 Supabase Auth,
ユーザーID:12345
可以確認他是該使用者。RLS 則會利用該使用者 ID,
申請者ID = 12345
只取得其對應的資料。
如此一來,即使同一張表格中有 1 萬筆申請資料,森本先生也只會看到自己的申請。
最容易理解的是「只限本人的資料」
在 RLS 中,最容易想像的模式是 只顯示本人資料。
例如會員專用的個人頁面,
- 本人的登錄資訊
- 本人的申請紀錄
- 本人的合約資訊
- 本人的諮詢紀錄
- 本人上傳的檔案
僅會顯示這些內容。
應用程式端當然也會指定使用者 ID 來取得資料,但不應只依賴於此,還要在資料庫端設定**「僅限已登入的本人資料」**這項規則。
在應用程式與資料庫兩端都進行控管是很重要的。
即使畫面端的實作出現錯誤,也能透過 RLS 確保無法取得其他使用者的資料。
依公司分開
在供交易對象使用的入口網站等情境中,有時不會以使用者為單位,而是以公司為單位來呈現資料。
舉例來說,
A株式会社
├ 森本さん
├ 大塚さん
└ 山田さん
假設有三位使用者,三人都能看到 A 股份有限公司的專案,但無法看到 B 股份有限公司的專案,這就是此控制方式。在資料上,則會建立如下的關聯:
ユーザー → 所属会社 → 案件
。接著在 RLS 端,
設定只能存取已登入使用者所屬公司的資料。
這是取引先向けポータル常見的架構,即使數十家、數百家公司使用同一個 Web 應用程式,依登入的公司不同,可見的資料也會隨之改變。
依據點分開
當業務應用程式逐漸成形,除了公司之外,據點也會納入考量。例如:
本部
東京支店
├ 担当者A
└ 担当者B
大阪支店
├ 担当者C
└ 担当者D
的架構。
- 東京分公司的負責人只能看到東京分公司的資料。
- 大阪分公司的負責人只能看到大阪分公司的資料。
- 總公司可以查看全部資料。
這是店鋪管理、營業所管理、代理商管理等常見的結構。從這裡開始,
ユーザー → 会社 → 拠点 → 役割 → アクセスできるデータ
便會進入權限設計的階段,在業務應用程式中,這項權限設計相當重要。
管理員可以檢視所有資料
當然也會有管理者這個角色。舉例來說,
- 一般使用者
- 據點管理者
- 本部管理者
- 系統管理者
等,像這樣區分權限。
- 一般使用者只能存取自己的資料。
- 據點管理者則可存取自己所屬的據點。
- 總部管理者可存取所有據點。
- 系統管理者則可進行使用者管理等操作。
等形式。
一般ユーザー → 自分だけ
拠点管理者 → 所属拠点
本部管理者 → 全拠点
這些規則不僅要在應用程式端設定,也要套用到 RLS。
僅隱藏管理畫面的選單並無法達到權限控制,因此必須確保使用者無法直接存取資料本身。
不僅控制「能否檢視」,也控制「能否變更」
RLS 並不只用於資料的檢視。
例如在申請應用程式中,
一般員工可以:
- 建立自己的申請
- 編輯自己的草稿
- 已核准的申請無法變更
主管則可以:
- 檢視下屬的申請
- 可進行核准或退回
總部管理員可
- 檢視所有項目
等規則。也就是說,
見る・登録する・変更する・削除する
需要針對各個角色分別思考,不能只考慮「這個人可以看這份資料」,還必須把在業務上該如何處理該資料也納入權限設計。
不僅是瀏覽,還需要設計新增、更新等操作權限。
不要只在應用程式端管理權限
例如在前端,
if 管理者 → 管理ボタンを表示
這樣做本身很常見,但僅靠畫面上的顯示控制是不夠的。
- 即使瀏覽器上看不到按鈕,如果有人直接呼叫 API,該怎麼辦?
- 如果有人直接指定 URL 來存取,該怎麼辦?
- 如果有人指定其他使用者 ID 來取得資料,該怎麼辦?
這類問題時有所聞。因此實際上,
画面
↓
アプリケーション
↓
API
↓
RLS
↓
Database
需要在多個地方進行確認。
RLS 的角色,是在最底層、最接近資料庫的位置保護資料。
在業務應用程式中,重要的不是「畫面上隱藏了,所以沒問題」,而是從一開始就讓資料無法被取得。
僅靠 RLS 並不能解決所有問題
反過來說,也沒有必要只靠 RLS 來實現所有的權限控制。
舉例來說,
- 是否可以開啟此畫面?
- 是否可以執行此 API?
- 是否要顯示此按鈕?
- 在執行此處理之前,確認是否已獲得核准。
應用程式端也會進行判斷。
若使用 Cloudflare Workers,有時也會在 Workers 端處理與外部 API 的通訊及伺服器端的處理。
因此實際上,
Cloudflare
↓
Next.js
↓
Supabase Auth
↓
アプリケーション側の権限確認
↓
RLS
↓
Database
大致是這樣的做法。
與其試圖在單一地方解決所有問題,不如在各個層級守護所需的部分。
管理處理中,有時也會不經過 RLS
這裡稍微談一下技術層面的內容。
在業務應用程式中,除了使用者本人操作的處理之外,還有由系統端執行的處理。
舉例來說,
- 管理員用的批次處理
- 批次處理
- 與外部系統的整合
- 資料遷移
- 管理用 API
等等。
在這類處理中,有時也會需要在不受 RLS 限制的情況下處理資料。
Supabase 提供了伺服器端管理處理所使用的權限,但這些權限當然不應暴露在瀏覽器端。
ユーザーからの操作
↓
RLSを通す
管理・システム処理
↓
サーバー側で必要な権限を使う
像這樣分開設計。
如果什麼都用管理權限執行,就失去了設定 RLS 的意義。
在業務應用程式中,首先建立權限表
在撰寫 RLS 之前,先整理業務上的權限。
例如,申請類應用程式大致會是這樣。
自分 拠点 全体 承認
一般ユーザー ○ × × ×
上長 ○ ○ × ○
本部管理者 ○ ○ ○ ○
實際上會更細緻,
- 瀏覽
- 註冊
- 更新
- 刪除
- 核准
- 輸出
等分類。
決定這些之後,
どのテーブルに
どんな情報を持たせるか
↓
どのユーザーが
どの行にアクセスできるか
↓
RLSをどう設定するか
的時候再來考慮。
在設定 RLS 之前,先整理業務上的權限。
RLS 只是把業務上決定的權限落實到資料庫的一種機制。
權限設計最好一開始就考慮,比較省事
在開發業務應用程式時,常會出現「先讓所有人都能瀏覽,之後再加上權限」的討論。
如果只是小型的原型,這樣做也無妨,但若考慮到正式營運,最好還是及早規劃權限設計,會比較省事。
之後,
- 分割公司
- 分割據點
- 新增管理者
- 新增代理商
- 新增外部人員
有時就必須連資料結構本身一起調整。
使用 Supabase 的 RLS 時也是如此,
誰與誰、以什麼單位共享資料
若能事先釐清這點,設計會變得相當清晰。
總結
使用 Supabase 的 RLS,可以將業務應用程式所需的細緻資料存取控制交由資料庫端處理。
舉例來說,
- 即可只顯示使用者本人的資料
- 僅顯示同一公司的資料
- 只顯示同一據點的資料
- 管理員可檢視所有資料
- 僅特定人員可更新
等控制。
Supabase Auth 負責確認「使用者是誰」,RLS 則控制「該使用者可以處理哪些資料」。
透過這樣的組合,可以實作業務應用程式所需的存取控制。
但真正重要的,並不是撰寫 RLS 本身。
首先,
誰能做什麼、能做到哪個範圍
這是在整理業務上的權限。
一旦這些確定,Supabase Auth、應用程式端的權限控制,以及 RLS 的角色也會自然隨之確定。
下一次將介紹為什麼要結合 Cloudflare Workers 與 Supabase。
平時較為隨性自在,但對於在意的事情,會一直查到自己理解為止。今天也在不斷嘗試與修正中,安靜地投入開發工作。
Matsun
前端工程師