iT邦幫忙

2026 iThome 鐵人賽

DAY 5
0
Modern Web

我推的Laravel S2!系列 第 5

我推的Laravel S2|Day 04:用 Starter Kit 快速起手:從 Breeze/Jetstream

  • 分享至 

  • xImage
  •  

一句話破題

「開新專案要不要順便裝認證?裝哪一套?」這題的標準答案在 Laravel 12 之後整個換了:Breeze 和 Jetstream 停更、官方文件連介紹頁都撤掉了,取而代之的是一套全新的 Starter Kit 系統,還多了一個叫 WorkOS AuthKit 的選項可以讓你完全不用自己處理密碼管理。今天我們用新版 Starter Kit 把 blog-app 的認證系統建起來,並且深入到「密碼規則怎麼客製化」「登入失敗要不要限流」這種實務上一定會遇到的細節。

先講一件事:Breeze/Jetstream 停更了

如果你看過第一季,那時候我介紹的第三方專案產生器「Laravel initializer」現在已經 404 了,裡面提到的 Breeze / Jetstream / Fortify 三選一組合也不再是標準答案。官方文件目前的說法是:

Laravel 12 引進了全新的 React、Svelte、Vue、Livewire 官方 Starter Kit(含選配的 WorkOS AuthKit 認證),Laravel Breeze 和 Laravel Jetstream 從此不再收到更新

兩層意思要分清楚:

  1. 是「停更」不是「移除」。舊專案已經裝了 Breeze/Jetstream 還是能正常運作,composer require laravel/breeze 目前也還能裝——但官方不會再加新功能或修 bug,長期而言你會希望遷移。
  2. 新專案的官方建議完全換了一套。打開 laravel.com/docs/13.x/starter-kits,已經找不到 Breeze/Jetstream,取而代之的是 React / Svelte / Vue / Livewire 四種 Starter Kit。

如果你手上有舊專案,要不要遷移?

這是個實務上常被問到、但答案沒那麼絕對的問題。幾個判斷點:

  • 如果專案已經穩定運作、沒有新的認證需求:不用急著遷移。Breeze/Jetstream 停更代表「不會有新功能」,不代表「現有功能會壞掉」,Laravel 框架本身的向下相容承諾依然適用。
  • 如果專案正在規劃大改版、或需要企業 SSO/Passkey 這類新版才有的能力:這時候遷移的成本效益比較合理,因為你反正要動這塊,不如順便換到有未來性的方案。
  • 遷移的實際工作量:不是簡單的「解除安裝 Breeze,安裝新 Kit」——你需要重新檢視所有跟認證相關的路由、view、以及任何客製化過 LoginController/RegisterController 的地方,因為新 Kit 的架構(Fortify 分離前後端)跟 Breeze(前後端邏輯常常寫在同一個 Controller)並不完全對應。這通常是一個獨立的技術任務,需要抓時間評估,不建議跟其他功能開發混在一起做。

認識新版 Starter Kit

設計哲學跟 Breeze 一樣(給你完整原始碼、不是黑盒套件),但技術堆疊全面升級:

Starter Kit 前端技術 UI 元件庫
React Inertia 3 + React 19 + TypeScript shadcn/ui
Svelte Inertia 3 + Svelte 5 + TypeScript shadcn-svelte
Vue Inertia 3 + Vue 3 Composition API + TypeScript shadcn-vue
Livewire Livewire 4 + Blade Flux UI

四種 Kit 有一個共同點:認證邏輯全部交給 Laravel Fortify 處理,Fortify 提供登入、註冊、忘記密碼、Email 驗證、雙因子驗證(2FA)等一整套 route/controller 邏輯,四種 Kit 只是在這個共同的認證後端上面,包了不同的前端外皮。

四種 Starter Kit(React/Svelte/Vue/Livewire)都是不同的前端外皮,底層共用同一套 Laravel Fortify 認證邏輯,可再選配 WorkOS AuthKit

「給你完整原始碼」這句話的實際含義

Breeze/新版 Kit 跟 Jetstream/傳統認證套件最大的哲學差異,在於程式碼的所有權。傳統套件(例如早期的 laravel/ui)把認證邏輯打包成一個黑盒子,裝進 vendor/ 目錄,你要客製化行為得靠套件開放的設定選項或事件監聽,改不動的地方就真的改不動。Starter Kit 走的是完全相反的路線:安裝的當下,所有 Controller、View、路由檔案都直接複製進你的 app/resources/routes/ 目錄,變成「你自己寫的程式碼」——裝完之後,Kit 本身的套件依賴其實很少,你看到的每一行認證邏輯都可以自由修改,不需要考慮「這樣改會不會被下次套件更新覆蓋掉」。

這個設計哲學的代價是:Starter Kit 不會隨著 Laravel 版本自動升級認證邏輯,因為那些程式碼已經是你的了,不再是套件的一部分。如果之後 Fortify 或前端框架有安全性更新,需要你自己比對變更、手動套用到你已經客製化過的程式碼裡——這是換取完全控制權必然要付出的維護成本,跟「裝一個黑盒套件、等它發新版」的模式是完全不同的取捨。

建立專案:選擇你的 Starter Kit

安裝流程現在全部透過官方 laravel CLI 工具完成——今天就是這系列真正建立 blog-app 的一天(Day 2 那個 hello-laravel 只是用來驗收環境的拋棄式專案,確認過環境沒問題就可以刪掉了):

composer global require laravel/installer
laravel new blog-app

執行 laravel new 之後,安裝程式會用互動式問答讓你選擇:

  1. 要用哪個 Starter Kit(React / Svelte / Vue / Livewire,或選「None」建立最陽春的空白骨架)
  2. 如果選了上述四種之一,還會問你要不要啟用 WorkOS AuthKit
  3. 資料庫選項(SQLite 是預設值,這點延續了 Laravel 11 起的新專案預設)
  4. 是否要順便建立 Git repository、跑第一次 npm install

https://ithelp.ithome.com.tw/upload/images/20260810/20163286ngpjIHhUdV.png

選好之後,安裝程式會自動跑完整套設定,完成後你只需要:

cd blog-app
npm install && npm run build
composer run dev

composer run dev 是 Starter Kit 附帶的一個方便指令,會同時啟動 PHP 開發伺服器、Vite 前端編譯監看、Queue Worker 等多個開發用行程。這背後其實是 composer.jsonscripts 區塊定義的一個組合指令,如果你想知道它實際做了什麼,可以打開 composer.json 查看 dev 這個 script 的內容——通常會看到它用 npx concurrently 同時起了好幾個行程,這也是為什麼你只要按一次 Ctrl+C 就能把所有開發用行程一起關掉。

這個系列選用 Livewire Starter Kit,理由很單純:後續 30 天的內容重心是後端概念(Eloquent、Middleware、Queue、Service Container……),Livewire 讓我們可以完全留在 Blade/PHP 的世界裡示範互動式介面,不需要額外解釋 React/Vue 的前端知識。如果你偏好 React/Vue/Svelte 生態,後續文章的後端程式碼一樣適用,只是畫面呈現的部分需要自行對應轉換。

Fortify:新版認證系統的心臟

不管你選哪個前端 Kit,實際處理登入/註冊邏輯的都是 Fortify。裝好專案後,你可以在 config/fortify.php 看到一個 features 陣列,決定啟用哪些認證功能:

use Laravel\Fortify\Features;

'features' => [
    Features::registration(),
    Features::resetPasswords(),
    Features::emailVerification(),
    Features::twoFactorAuthentication([
        'confirm' => true,
        'confirmPassword' => true,
    ]),
],

想關掉某個功能(例如不開放自行註冊,帳號都由後台建立),把對應那行註解掉或刪除即可。如果你用的是 React/Svelte/Vue Kit,記得同步把前端頁面裡引用到該功能路由的程式碼也移除,因為這幾個 Kit 用 Wayfinder 在編譯期產生型別安全的路由定義,引用到不存在的路由會導致建置失敗——這是新版 Kit 特有的細節,Livewire Kit 因為是伺服器端渲染,沒有這個限制。

真正處理「怎麼建立一個新使用者」「密碼規則長怎樣」的程式碼,放在 app/Actions/Fortify/ 目錄下:

檔案 用途
CreateNewUser.php 驗證輸入並建立新使用者
ResetUserPassword.php 驗證並更新使用者密碼
UpdateUserPassword.php 已登入使用者主動更改密碼
UpdateUserProfileInformation.php 更新姓名/Email 等基本資料
PasswordValidationRules.php 定義密碼驗證規則

如果 blog-app 之後想在註冊表單加一個「暱稱」欄位,直接改 CreateNewUser::create() 方法即可——認證邏輯集中在這個目錄,不用到處找。

實際走一遍:客製化密碼規則

假設 blog-app 的營運方要求密碼規則要比預設更嚴格(至少 10 個字元、必須包含符號),打開 PasswordValidationRules.php

// app/Actions/Fortify/PasswordValidationRules.php
trait PasswordValidationRules
{
    protected function passwordRules(): array
    {
        return [
            'required',
            'string',
            Password::min(10)
                ->letters()
                ->mixedCase()
                ->numbers()
                ->symbols()
                ->uncompromised(),   // 檢查是否出現在已知外洩密碼資料庫
            'confirmed',
        ];
    }
}

Password::min(10)->letters()->mixedCase()->numbers()->symbols() 這條鏈式呼叫是 Laravel 內建的 Illuminate\Validation\Rules\Password 規則物件,uncompromised() 這個方法特別值得認識——它會透過 Have I Been Pwned 的 k-Anonymity API,檢查使用者輸入的密碼是否出現在已知的資料外洩清單裡(傳輸過程只送出密碼雜湊值的前 5 碼,不會外洩完整密碼),如果命中就拒絕這組密碼。這個規則不需要額外的第三方帳號或金鑰,是提升密碼安全性成本最低的一步。

這個 passwordRules() trait 同時被 CreateNewUser(註冊)和 UpdateUserPassword(改密碼)共用,改一處就能同步套用到所有需要密碼驗證的情境,這是這套架構刻意設計成 trait 而非各自硬寫的原因。

雙因子驗證:使用者實際會看到什麼

Features::twoFactorAuthentication() 啟用後,使用者可以在個人設定頁面開啟 2FA,流程大致是:

  1. 使用者點擊「啟用雙因子驗證」,系統產生一組 QR Code,使用者用 Google Authenticator 之類的 App 掃描。
  2. confirm: true 這個選項代表使用者必須輸入一次驗證碼確認設定成功,才算真正啟用(避免使用者掃了 QR Code 但沒有正確設定好 App,之後被鎖在帳號外面)。
  3. confirmPassword: true 代表使用者要開啟/關閉 2FA 這種敏感操作前,系統會要求重新輸入一次密碼確認身份(即使使用者已經登入中)。
  4. 啟用成功後,系統會產生一組復原碼(recovery codes),提醒使用者妥善保存——這是使用者手機遺失、無法產生驗證碼時的最後救生索。

這一整套流程完全由 Fortify 處理,blog-app 不需要自己寫任何 TOTP(Time-based One-Time Password)演算法邏輯,這也是選擇官方認證方案而不是自己從頭刻的主要理由之一:2FA 這類牽涉密碼學細節、稍有差錯就是資安漏洞的功能,交給經過廣泛審查的官方實作遠比自己寫更值得信賴。

雙因子驗證啟用流程:產生 QR Code → 輸入驗證碼確認 → 敏感操作前重新確認密碼 → 產生復原碼完成啟用

登入嘗試的流量限制

Fortify 預設已經幫你設定好登入失敗的節流保護,定義在 app/Providers/FortifyServiceProvider.php(或視 Starter Kit 版本,可能整合進 AppServiceProvider):

use Illuminate\Cache\RateLimiting\Limit;
use Illuminate\Support\Facades\RateLimiter;

RateLimiter::for('login', function (Request $request) {
    $throttleKey = Str::transliterate(Str::lower($request->input(Fortify::username())).'|'.$request->ip());

    return Limit::perMinute(5)->by($throttleKey);
});

這條規則同時考慮了「使用者名稱」跟「IP 位址」兩個維度組成節流的 key,用意是防止兩種不同的暴力破解手法:針對單一帳號的密碼字典攻擊(同一個帳號被大量嘗試不同密碼)跟針對單一 IP 的帳號列舉攻擊(同一個來源嘗試大量不同帳號)。這條規則跟 Day 7 會講到的 RateLimiter::for() 是同一套機制,這裡先讓你知道 Fortify 已經內建了這層保護,不需要自己額外實作。

WorkOS AuthKit:不想自己管密碼的另一條路

如果你的應用需要企業級的 SSO、想支援 Passkey(無密碼登入)、或想要「一鍵用 Google/Microsoft/GitHub/Apple 登入」,新版 Starter Kit 有一個內建的 WorkOS AuthKit 變體。安裝時選擇 WorkOS 選項,Laravel 就會把整個認證流程委託給 WorkOS 這個第三方服務,你的應用程式完全不需要處理密碼儲存。

啟用後需要到 .env 補上三個變數(值來自 WorkOS 後台):

WORKOS_CLIENT_ID=your-client-id
WORKOS_API_KEY=your-api-key
WORKOS_REDIRECT_URL="${APP_URL}/authenticate"

WorkOS 對月活躍使用者 100 萬以下的應用程式提供免費額度,適合新創或個人專案先用免費方案把 SSO/Passkey 這種通常很花工程時間的功能直接外包掉。

啟用 WorkOS AuthKit 之後,blog-app 的使用者資料模型還是原本的 User,但密碼欄位基本上不會被使用——使用者的身份驗證完全發生在 WorkOS 的託管頁面,你的應用程式只負責在使用者驗證成功後,接收 WorkOS 回傳的身份資訊並建立/更新對應的本地 User 紀錄(這個流程通常叫做「Just-In-Time Provisioning」)。這代表如果你之後想要「先用 WorkOS,之後又想加回傳統密碼登入」,需要額外處理「這個帳號到底該用哪種方式驗證」的邏輯,不是單純切換設定就好,這是選擇 WorkOS 前值得先想清楚的架構決策。

Socialite 呢?跟 WorkOS AuthKit 有什麼不同

兩者容易搞混,先講清楚定位差異避免選錯:Socialite 適合「單純想加個 Google/GitHub 登入按鈕」的情境,WorkOS AuthKit 適合需要企業 SSO、Passkey、或多種登入方式一次到位的情境。WorkOS 不是要取代 Socialite,兩者可以並存,依需求選擇(Socialite 的實際用法 Day 28 會示範)。

更具體一點的判斷方式:如果你的產品是面向一般消費者(B2C,例如 blog-app 這種部落格平台),使用者多半只是想快速用既有的 Google 帳號登入,不需要 SSO 這種企業級功能,Socialite 更輕量、實作也更直觀。如果你的產品是面向企業客戶(B2B SaaS),客戶公司的 IT 部門通常會要求「員工登入你的系統要走公司自己的 Okta/Azure AD」,這種情境正是 WorkOS AuthKit 設計要解決的問題——它原生支援 SAML/OIDC 這些企業 SSO 標準協定,而 Socialite 主要是針對消費級社群平台(Google、GitHub、Facebook 等)的整合,兩者的目標客群跟技術複雜度完全不在同一個量級。

常見錯誤與踩雷點

  • 舊教學文章寫著 laravel new blog-app --breeze 之類的參數:這是舊版 installer 的用法,新版 installer 是互動式問答,不再用這類 flag 指定 Breeze/Jetstream。如果你想用社群維護的其他 Starter Kit,正確參數是 laravel new my-app --using=vendor/package-name
  • 選了 React/Vue/Svelte Kit,改完 config/fortify.php 卻編譯失敗:多半是忘記同步刪除前端頁面對已停用功能路由的引用,前面提過的 Wayfinder 型別安全路由機制會在建置期直接報錯,這其實是件好事——它幫你在部署前就抓到不一致,而不是等使用者點到壞掉的連結。
  • 誤以為新專案還是預設帶 Sanctum:這在幾年前成立,現在不成立了。新版 Starter Kit 的認證基礎是 Fortify,Sanctum(處理 API Token/SPA 認證)需要另外安裝,Day 28 會示範。
  • 改了 passwordRules() 卻忘記現有使用者的舊密碼不受影響:加嚴密碼規則只會套用到「之後」的註冊與改密碼流程,資料庫裡既有使用者的舊密碼雜湊不會被強制要求重新設定,如果你的產品規範要求全體使用者跟進新規則,需要另外設計強制改密碼的流程(例如下次登入時導向改密碼頁面),這不是 Fortify 自動幫你做的事。
  • uncompromised() 規則在本機開發環境噴出網路錯誤:這個規則需要連外呼叫 Have I Been Pwned 的 API,如果本機環境沒有對外網路(例如某些企業內網開發環境),驗證會失敗。開發/測試環境可以考慮用 App::environment() 判斷條件式地跳過這條規則,正式環境保留。
  • 開了 2FA 的 confirmPassword: true,卻讓使用者在 API 情境下卡住:如果 blog-app 之後有原生 App 或 SPA 透過 API 呼叫這些敏感端點,confirmPassword 這種依賴 Session 重新確認密碼的機制,需要對應調整成 API 友善的實作方式(例如另外設計一個帶密碼的請求端點),不能直接假設前端一定有一個「請再輸入一次密碼」的網頁表單可以用。

小結

新版 Starter Kit 用 Fortify 統一了認證後端邏輯,前端則開放 React/Svelte/Vue/Livewire 四選一,外加選配的 WorkOS AuthKit 處理企業級認證需求。今天除了認識整體架構,也實際走過密碼規則客製化、2FA 使用者流程、登入節流保護這幾個實務上一定會遇到的細節。Breeze/Jetstream 沒有被砍掉,但已經是「維護模式」的舊技術,新專案沒有特別理由的話直接用新版 Starter Kit。我們的 blog-app 從今天起就是一個用 Livewire Starter Kit 建立、Fortify 驅動認證的專案。

站在巨人的肩膀上,才能看得更遠 — 牛頓

明日預告

Day 5 打開 blog-app 的目錄結構,你會發現少了很多熟悉的檔案——Kernel.php 去哪了?答案都在 bootstrap/app.php 這個新骨架的核心裡。


上一篇
我推的Laravel S2|Day 03:開發工具全家桶:Laragon、Herd、Docker、Sail、VS Code
下一篇
我推的Laravel S2|Day 05:全新應用程式骨架:bootstrap/app.php 與目錄結構
系列文
我推的Laravel S2!7
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言