Skip to main content

Command Palette

Search for a command to run...

Lesson 10: 守門員的權限:授權 - RBAC 與 Policy 設計

Updated
3 min read

在上一堂課,我們學會了如何驗證「你是誰」(Authentication)。但確認身分只是第一步。

想像你是一間銀行的員工。你刷卡通過大門(Authentication 成功),但这不代表你可以直接走進金庫搬錢。「你能做什麼?」 這就是 授權 (Authorization) 的範疇。

對於後端工程師來說,如果說 Authentication 是守大門的警衛,那麼 Authorization 就是每一扇門上的電子鎖與守門員,它決定了請求是否會被拒絕(403 Forbidden)。

核心觀念:Authentication vs. Authorization

特性Authentication (驗證)Authorization (授權)
關鍵問題你是誰? (Who are you?)你可以做這件事嗎? (Can you do this?)
失敗狀態碼401 Unauthorized (通常指未登入)403 Forbidden (已登入但權限不足)
發生時機請求最前端,確認身分確認身分後,執行具體邏輯前
生活案例出示護照通關持有頭等艙機票進入貴賓室

RBAC:角色基礎存取控制 (Role-Based Access Control)

在小型專案中,你可能會寫出這樣的程式碼:

if ($user->id === 1) {
    // 允許刪除文章
}

這種寫法難以維護。當系統變大,我們需要更結構化的管理方式。這就是 RBAC

什麼是 RBAC?

RBAC 的核心概念是:權限不直接綁定在「人」身上,而是綁定在「角色」身上。

  1. User (使用者):系統的操作者。

  2. Role (角色):一組權限的集合(例如:管理員、編輯、一般會員)。

  3. Permission (權限):具體可以執行的動作(例如:刪除文章、編輯商品)。

資料庫設計範例

一個標準的 RBAC 通常涉及五張表(或簡化為三張):

  1. users (使用者)

  2. roles (角色)

  3. permissions (權限) - 選用,簡單系統可直接定義在 Role 上

  4. role_user (使用者與角色的關聯 - 多對多)

  5. permission_role (角色與權限的關聯 - 多對多)

透過這種設計,當有新進員工時,你不需要逐一開啟 50 個功能的權限,只需將他指派為「編輯 (Editor)」角色即可。

RBAC 補充:權限粒度與層次

RBAC 核心概念是 權限不綁定個人,而是綁定角色。在實務中,RBAC 的設計還可以進一步補充以下概念:

1. Permission 粒度

  • 粗粒度角色:Admin、Editor、User

    適合小型系統或權限需求單純的情境。

  • 細粒度權限:例如「刪除文章」「編輯文章」「審核留言」

    好處是更靈活,角色可以組合多個細節權限,不必新增角色就能控制更多行為。

2. 層次化角色 (Hierarchical RBAC)

  • 大型系統中,角色可以繼承權限。

    例如:

      User < Editor < Admin
    
  • Editor 自動擁有 User 權限,Admin 擁有 Editor + User 權限

  • 好處:減少重複設定,維護方便

3. 動態角色與條件權限

  • 有些權限需要依情境動態授予,例如:

    • 專案負責人可以編輯專案

    • 訂單管理員可以修改自己部門的訂單

  • 這通常會搭配 Policy 或 Gate 做進一步檢查

總結:RBAC 不只是 User → Role → Permission,更是一個 設計思維,可以控制粒度、層次與動態條件。

Policy 設計:將業務邏輯與控制器分離

在現代後端框架(如 Laravel, Django, NestJS)中,我們通常不會在 Controller/Service 裡面寫又臭又長的 if-else 來檢查權限。我們使用 Policy (策略模式)

Policy 的核心目的,就是將「能不能做 (Can)」的邏輯,從「實際去做 (Do)」的邏輯中拆分出來。

這裡不看code,真的很難繼續講下去,我們來看個code 經典範例吧。

以下範例code由Laravel 12 實現。

情境:部落格文章的修改權限

假設規則如下:

  1. 管理員 (Admin) 可以修改任何人的文章。

  2. 作者 (Author) 只能修改「自己」寫的文章。

  3. 一般人 不能修改文章。

Bad Smell Code

我們當然可以這樣寫

//Service
$post = $this->postRepository->find($postId);

if($user->role == User::ADMIN || $post->created_by == auth()->user()->id){
    $this->postRepostory->update($updateData , $post->id);
}else {
  abort(403);
}

//Repository
function update($data, $postId)
{
    return $this->model::where('id', $postId)->update($data);
}

但有些bad smell 對吧?

很明顯有:可讀性與維護性缺失,業務邏輯 和 授權邏輯 混在一起,程式碼會變得越來越長。

Modify Step

  1. 新增Policy檔案
php artisan make:policy PostPolicy --model=Post
#--model=Post 可選
  1. (可選)如果Model File Name沒有符合Laravel auto load

我這裡故意用Postssss當Model Name來說明

//AppServiceProvider
Gate::policy(Postssss::class, Postolicy::class);

//或者
#[UsePolicy(PostPolicy::class)]
class Postssss extends Model
{
}
  1. Policy Update
public function update(User $user, Post $post)
{
   return $user->role === User::ADMIN
        || $post->created_by === $user->id;
}

或者

class PostPolicy
{
    /**
     * 在所有其他檢查之前執行
     * 如果回傳 true,則直接允許;回傳 null 則繼續往下檢查
     */
    public function before(User $user, $ability)
    {
        // 規則 1: Admin 可以修改任何人的文章
        if ($user->role === User::ADMIN) {
            return true;
        }
    }

    /**
     * 判斷 User 是否可以更新 Post
     */
    public function update(User $user, Post $post)
    {
        // 規則 2: 作者只能修改「自己」的文章
        // 規則 3: 一般人不能修改 (因為不是 Admin 也不是作者,這裡會回傳 false)
        return $user->id === $post->created_by;
    }
}
  1. 修改Service
$post = $this->postRepository->find($postId);

Gate::authorize('update', $post);

$this->postRepository->update($updateData, $postId);

這個會回傳 在 Policy中的return

auth()->user()->can('update' , $post);

這個會回傳 Auth/Access/Response

$response = Gate::inspect('update' , $post)
$response->allowed()

業務邏輯與權限邏輯分離比較

項目Service 判斷使用 Policy
權限判斷位置ServicePolicy(官方建議)
可維護性中等高(集中管理)
擴展角色邏輯需要修改 Service修改單一 Policy
可讀性可以,但會累積清楚:Service=流程、Policy=權

常見陷阱

隱藏不等於後端安全: 不要以為在前端把「刪除按鈕」藏起來,駭客就刪不掉資料。後端 API 每一支 修改資料的接口都必須有 Authorization 檢查。

  1. IDOR (Insecure Direct Object References): 這是最常見的漏洞。例如 API 是 GET /orders/1234,如果後端只檢查「使用者是否登入」,而沒檢查「訂單 1234 是否屬於該使用者」,那麼惡意使用者可以遍歷 ID 偷看別人的訂單。Policy 就是防止 IDOR 的最佳防線。

  2. 超級管理員 (Super Admin): 設計 Policy 時,可以設計一個 before 過濾器,讓 Super Admin 繞過所有檢查,避免你在開發時把自己鎖在外面。

從Rookie到Junior,一個後端成長的30堂課

Part 19 of 31

“從 Rookie 到 Junior:一個後端成長的 30 堂課” 是一套專為後端新手所設計的成長型技術系列文章。內容以實務為導向,逐步拆解後端工程的核心能力,包括程式語言基礎、架構思維、框架運作原理、業務邏輯設計、資料庫操作、以及常見的開發模式。 本系列的目標是協助讀者從零散的學習堆疊,建立成體系的後端知識框架。讀者能夠理解各語言背後不變的工程思維與設計原則。這套內容旨在讓學習者從「會寫程式」進入「能理解系統設計」的階段,逐步具備勝任 Junior Backend Engineer 的能力。

Up next

Lesson 9: 守門員的識別證:驗證 - Session vs JWT

在Lesson 7的時候有提到 「HTTP是無狀態性的」,那大家都有豐富的網站使用經驗,如果每次請求都是無狀態,那我們大家如何分辨自己的訂單?自己的購物車呢? 所以其實我們會有 認證身分的方式,所以這篇文章就來介紹:Session vs JWT。 Session Session 是一種歷史悠久且穩定的身分識別方式,雖然我們叫Session,但其實多半也會需要依靠Cookie的幫忙,但其實Cookie是可以且容易被竄改的,因此實際上還會有其餘的資安防護手法。 Session像是健身房的置物櫃鑰匙。...

More from this blog

Lesson 26 : 系統韌性的守護者-限流、熔斷與背壓的設計模式

當這幾個名詞出現後,代表我們進到了一個高併發/大流量的系統了。在這個章節中,我們一起來看看如何透過一些方式來避免高併發導致我們的系統crash掉。 限流(Rate Limiting) 相信大家對這個名詞並不陌生,限流其實就是字面上的含意,限制流量。 限流的目的是保護「接收方」,確保系統不會因為瞬間的高併發請求而癱瘓。 限流通常發生在 API Gateway 或服務的最前端。它像是一個夜店門口的保全

Mar 26, 20262 min read

Lesson 25: 淺談 單體架構、微服務架構與單/多租戶架構

過去我們討論了 要把程式寫在哪、程式要怎麼拆的題目,接下來我們來看看「如何服務不同客戶」,這些架構反映了軟體開發在擴充性與複雜度之間的權衡。 軟體架構深度解析:從系統拆分到商業規模化 在軟體工程的演進中,架構的選擇往往是在「開發效率」、「系統擴充性」與「營運成本」之間尋求平衡。我們可以從兩個核心維度來觀察這些架構:系統如何運行(單體 vs. 分散式) 以及 如何服務客戶(單租戶 vs. 多租戶)。

Mar 26, 20262 min read

Lesson 24: 資料庫擴展術-讀寫分離、複寫機制與快取一致性挑戰

為什麼要讀寫分離? 大多數的 Web 應用都是 「讀多寫少」(例如:看文的人多,發文的人少,Heavy Read System)。當所有的請求都塞給同一台資料庫時,磁碟 I/O 和連線數會成為瓶頸。 Master (主庫): 負責寫入 (Insert/Update/Delete),確保數據一致性。 Slave (從庫): 負責讀取 (Select),可以有多個從庫來分擔讀取壓力。 為什麼讀

Mar 25, 20262 min read

面試經驗談 2025-2026

從2025年3月開始,我陸陸續續參與了從新創到上市櫃公司的Senior - Tech Lead的相關面試,其中有不乏 尊重面試者、展現高度專業的企業(公司),當然也有遇到幾場面試鬼故事,這篇文章主要分享我對於軟體工程師面試的方向分享,以及部分鬼故事,以此警惕自己不要成為這樣的面試官。 AI的洪流,改變了SWE的生態 LLM的發展確確實實的影響到了軟體工程師的生態。 過去受限於算力與資料規模,深度學

Mar 23, 20262 min read

Lesson 23: 系統的緩衝區-Queue 佇列與非同步處理 (Asynchronous)

佇列 佇列的實作工具非常多,舉凡AWS SQS、RabbitMQ、Kafka…等。 佇列的特性,其實是一個非常強大的系統緩衝區,應用層面非常廣。 什麼是佇列? 佇列可以想像成,在既有流程中外,有另一個”水管”,來連接原有的資料流(或邏輯過程),其中 呼叫方將資料 推(Push)到水管中,接受方(監聽) 從水管中將資料拉(Pull)出處理 為什麼佇列是「強大的緩衝區」? 在同步處理中,系統像是一

Mar 23, 20262 min read

Bennett's Tech Blog | 後端架構、系統設計

32 posts

來自台灣的軟體工程師,相信軟體可以改變世界