RBAC 權限怎麼運作
這篇寫給第一次接觸慈恩園系統權限的人。
完整規格以 GitHub issue #246 為準;本頁已在 2026-06-30 對照當時的程式碼與測試。
先記住一句話
系統不是在程式裡寫死「會計可以做什麼、出納不能做什麼」,而是:
使用者打開某個網址或呼叫 API
→ 系統判斷這是哪一項功能
→ 系統判斷這次要查看、新增、修改、刪除還是匯出
→ 檢查使用者登入時拿到的權限
→ 決定放行或拒絕例如:
會計按下「開立發票」
→ 系統認出這是 AccountingReview(會計審核)
→ 這次操作屬於 U(修改)
→ 使用者有 AccountingReview:U
→ 放行這就是 issue #246 所說的:
route → feature → grant
網址/API → 功能權限 → 角色被授予的權限新人最常問的問題
為什麼畫面上沒有某個按鈕?
前端會先檢查權限,沒有權限就隱藏按鈕。
常見寫法:
const { canCreate, canUpdate } = usePermission('Customer')但是「隱藏按鈕」只是讓畫面比較合理,不是安全防線。有人仍可能直接呼叫 API,所以後端還會再檢查一次。
為什麼看得到按鈕,按下去卻出現 403?
通常表示前端和後端檢查的權限不同。
例如前端用 CertificatePurchase:U 顯示「開立發票」,但後端真正要求的是
AccountingReview:U。前端覺得可以按,後端卻拒絕,便會出現 403。
遇到這種問題,不要只改前端或只放寬後端;要先找出這個動作真正屬於哪個功能。
為什麼管理員改完角色權限,使用者還是不能操作?
權限會放進使用者登入時取得的 JWT。管理員改完角色授權後,該使用者必須 重新登入,才會拿到新的權限。
SystemAdmin 也需要逐項勾權限嗎?
不需要。SystemAdmin 在前端與後端都會直接放行。
這是特例;其他角色仍以實際授權為準。
權限字母代表什麼?
系統使用六個字母表示操作類型:
| 字母 | 白話說明 | 例子 |
|---|---|---|
| M | 可以從選單進入 | 看得到「客戶管理」選單 |
| V | 可以查看 | 查詢客戶、查看申請單 |
| C | 可以新增 | 新增客戶、建立申請 |
| U | 可以修改 | 編輯資料、審核、完成 |
| D | 可以刪除 | 刪除或作廢資料 |
| E | 可以匯出 | 匯出 Excel/報表 |
登入後的權限大致長這樣:
Customer:C,V
Application:M,V,U
AccountingReview:U意思是:
- 可以查看、新增客戶。
- 可以從選單進入申請單,並查看、修改申請單。
- 可以執行會計審核動作。
系統目前會自動把 M 一併視為 V:既然能進入功能頁,至少也要能讀取頁面資料。
後端實際怎麼判斷?
所有一般 API 都會經過 ApiPermissionFilter。
它依序處理:
- 這支 API 是否允許匿名使用?
- 使用者是否已登入?
- 帳號是否被停用,登入憑證是否已失效?
- 這支 API 是否只要求「有登入即可」?
- 使用者是否為
SystemAdmin? - 這次操作需要哪一個功能與操作權限?
- 使用者是否真的有該權限?
API 權限有三種判斷方式
1. API 自己明確寫出權限
重要或特殊功能會直接標示:
[FeatureAccess("CustomerSensitive", "U")]意思是:這支 API 要求「修改客戶敏感資料」權限。
這種方式最清楚,常用在客戶敏感欄位、印鑑卡、會計審核等特殊動作。
2. 共用 API 按網址自動判斷
系統很多功能共用 Query、Post、Put、Delete controller。
例如:
/Query/Customer → Customer:V
/Post/Customer → Customer:C
/Put/Customer → Customer:U少數動作不能只看功能名稱。例如權狀申購的「開立發票」雖然掛在
CertificatePurchase 網址下,實際上屬於會計工作,所以後端會改判成
AccountingReview:U。
3. 舊的專用 API 還沒有標權限
如果一支專用 controller 沒有寫 [FeatureAccess],也不是共用 API,
目前會暫時採「登入即可」。
後端同時會記錄:
[RBAC-DEDICATED-TODO]這是過渡機制,不是新 API 可以照抄的標準。
為什麼同一個功能還要拆出特殊權限?
因為「可以使用申請單」不代表「可以做會計審核」。
如果所有動作都只檢查 Application:U,行政、出納、會計可能互相做到不屬於
自己的工作。系統因此把敏感動作拆成更精確的功能權限:
| 權限 | 可以做什麼 | 主要授權對象 |
|---|---|---|
AccountingReview:U | 會計審核、處理發票 | 會計 |
CashierReview:U | 確認收款、出納退回 | 出納 |
ApplicationOverride:U | 完成、確認附件、操作他人建立的申請單 | 管理處/主管 |
CustomerSensitive:U | 修改客戶姓名、身分證字號 | 管理處/主管 |
SealCard:V,C,U | 查看、上傳、作廢印鑑卡 | 管理處/主管 |
這種「不同職務不能互相代做」的設計叫 SoD(職責分離)。
新人不需要先背 SoD;只要記得:
敏感動作應該有自己的功能權限,不能只借用所在頁面的通用修改權。
角色授權畫面和左側選單不是同一件事
有些權限只控制按鈕或特殊動作,不需要出現在左側選單。例如
AccountingReview、CustomerSensitive。
因此系統分成兩條資料:
- 角色授權畫面:列出所有可授權的功能,包括不顯示在選單的特殊權限。
- 使用者左側選單:只顯示設定為可見,而且使用者具有 M 權限的項目。
所以「不在左側選單出現」不代表「管理員不能授權」。
管理員可以在「受控動作權限(SoD)」群組中調整五項特殊權限。
有些限制不是按鈕權限
有些規則要看「這一筆資料屬於誰」,不能只看功能權限。
例如申請單:
- 建立者可以操作自己建立的申請單。
- 其他人必須另外具有
ApplicationOverride才能操作。
這類規則放在 service 裡判斷,稱為資料範圍或列級限制。
目前後台具有 Reservation 權限的角色可以查看全部訂位。未來若要做
「經銷商只能看自己的訂位」,也應在 service 依可靠的使用者/經銷商 ID
限制,不能拿顯示姓名來比對。
目前已知的問題
以下不是理論上的風險,而是 2026-06-30 對照程式碼後仍存在的情況。
1. 權狀申購的發票按鈕可能看得到但不能按成功
畫面目前用 CertificatePurchase:U 決定是否顯示會計操作面板,後端卻要求
AccountingReview:U。
可能症狀:
- 行政人員看得到發票按鈕。
- 按下後 API 回傳 403。
正確方向:前端也應使用 AccountingReview:U。
2. 沒有新增客戶權限的人仍可能進入新增頁
權狀申購、選位等頁面共用的客戶搜尋區塊會顯示「建立新客戶」,但目前沒有先
檢查 Customer:C。
可能症狀:
- 使用者可以進入新增客戶頁。
- 填完資料後,儲存時才被後端拒絕。
是否要授予會計/出納新增客戶權限仍需 PM 或管理員決定;不論決定為何,
前端按鈕都應與 Customer:C 對齊。
3. 部分電子簽核查詢目前只要登入就能呼叫
SignatureController 的建立動作已要求 Signature:C,但下列動作尚未標示
權限:
- 取得目前簽核
- 查看簽核歷程
- 驗證簽核鏈
它們目前會走前面提到的「舊 API 過渡放行」。
4. 舊版行動 App API 仍允許匿名
ZionApiController 整個 controller 仍標示 [AllowAnonymous]。
這是已知的舊系統安全債,不是一般 API 的開發範例。
5. 有一段註解已經過期
auth-store.ts 還寫著「敏感欄位/印鑑卡折回 Customer:U」,但實際程式已改用:
CustomerSensitiveSealCard
調查問題時應以實際 controller、前端元件與權限 contract 為準,不要只相信 這段舊註解。
修改權限時要檢查哪些地方?
如果新增功能或修改權限名稱,至少確認:
- 後端 API 最後判斷的是哪個功能與操作字母。
- 前端按鈕是否檢查同一個權限。
- 頁面路由守衛是否使用正確功能名稱。
rbac-features.json是否登錄該功能。- migration 是否建立
tb_menu功能與角色授權。 - 修改角色權限後,是否重新登入再測試。
- 是否分別使用有權限、沒有權限的角色實際操作。
不要只用 SystemAdmin 測試,因為 SystemAdmin 會跳過所有權限檢查。
給需要追程式的人
| 想查什麼 | 從哪裡開始 |
|---|---|
| 後端為什麼放行或回 403 | api/Filters/ApiPermissionFilter.cs |
| 網址如何轉成功能權限 | api/Filters/FeaturePermission.cs |
| 特殊 API 明確要求什麼權限 | 搜尋 [FeatureAccess(...)] |
| 前端按鈕怎麼判斷 | composables/usePermission.ts、auth-store.ts |
| 頁面進入權限 | middleware/auth.global.ts |
| 角色授權樹 | MenuService.GetAllMenusAsync、EditRoleModal.vue |
| 左側選單 | MenuService.GetUserMenuAsync |
| 系統有哪些正式功能權限 | contracts/rbac-features.json |
| DB 種子 | migrations 301–306 |
這篇內容怎麼驗證的?
2026-06-30 的檢查結果:
- issue #246 的核心設計已存在於
develop。 - SoD 主要程式與 migrations 303–306 已存在。
- RBAC 相關目標測試:29 個通過、0 個失敗。
- 本次沒有重新查詢 grace2,因此不把 issue 在 2026-06-27 記錄的 DB 驗證 當成本次即時結果。
ActionFeatureOverrides目前沒有直接單元測試,這部分是以程式碼對照確認。
名詞小抄
| 名詞 | 白話說明 |
|---|---|
| RBAC | 依角色分配功能權限 |
| feature | 一項可以被授權的功能,例如 Customer、AccountingReview |
| operation/op | 查看、新增、修改、刪除等操作字母 |
| grant | 管理員把某項權限授給角色 |
| JWT/claim | 使用者登入後隨身攜帶的權限資料 |
| route | 使用者進入的網址或呼叫的 API 路徑 |
| SoD | 職責分離,避免不同職務互相代做敏感工作 |
| 403 | 已登入,但沒有執行這項操作的權限 |