文件 #6
進行中模組 5:認證、授權與多團隊支援
是由 Jeffery Hsu 於 3 天 前加入. 於 2 天 前更新.
0%
概述
- Laravel Breeze / Jetstream / Livewire Starter Kit 認證。
- Passkey 與 Socialite 登入。
- Laravel 13 Teams 功能(多團隊切換、Scoped Data)。
- Authorization 在 Livewire 中的應用。
- 實作:帶團隊管理的會員系統。
JH 是由 Jeffery Hsu 於 3 天 前更新 動作 #1
- 追蹤標籤 從 功能 變更為 文件
JH 是由 Jeffery Hsu 於 2 天 前更新 動作 #2
- 狀態 從 新建立 變更為 實作中
進入 模組 5:認證、授權與多團隊支援!這是一個非常實務且核心的模組,無論你是做接案、SaaS 產品還是企業內部系統,這都是必經之路。
由於內容龐大,我將這個模組分為 三個部分 來逐步講解。
今天我們先進行 第一部分,專注於:Starter Kit 認證架構解析,以及實作 Laravel Socialite 第三方登入(以 GitHub 為例)。
第一步:Starter Kit 認證架構 (Breeze vs Jetstream)¶
在 Laravel 生態系中,建立會員系統不需要從零開始。
-
Laravel Breeze: 我們在模組 1 選擇的起手式。它非常輕量,預設提供了註冊、登入、忘記密碼、Email 驗證等基礎功能。它的程式碼完全發布在你的專案中(
app/Http/Controllers/Auth或 Livewire Volt 檔案),非常容易客製化。 - Laravel Jetstream: 更進階的選擇。除了 Breeze 的功能外,它原生內建了 Teams (多團隊) 功能、雙因素認證 (2FA) 以及 API Token 管理。
為了讓你深入理解底層邏輯,我們將以目前的 Breeze + Flux UI 架構為基礎,手動擴充這些進階功能(包含後續的多團隊系統),這樣你的基本功會最紮實。
第二步:準備 Socialite (第三方登入) 環境¶
現今的 Web App 通常會提供 Google、GitHub 或 LINE 登入。Laravel 官方套件 Socialite 讓這件事變得極度簡單。
1. 安裝 Laravel Socialite
在終端機執行:
composer require laravel/socialite
2. 修改 Users 資料表 (Migration)
我們需要在 users 資料表中加入欄位,用來記錄第三方平台的 ID。
建立一個新的 Migration:
php artisan make:migration add_socialite_columns_to_users_table
打開新產生的 Migration 檔案(位於 database/migrations/):
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
public function up(): void
{
Schema::table('users', function (Blueprint $table) {
$table->string('github_id')->nullable();
$table->string('auth_provider')->default('email'); // 記錄註冊來源
// 因為第三方登入不一定有密碼,所以將 password 改為 nullable
$table->string('password')->nullable()->change();
});
}
public function down(): void
{
Schema::table('users', function (Blueprint $table) {
$table->dropColumn(['github_id', 'auth_provider']);
$table->string('password')->nullable(false)->change();
});
}
};
執行遷移:
php artisan migrate
(注意:若執行報錯提示需要 doctrine/dbal 才能修改欄位,請先執行 composer require doctrine/dbal)
3. 更新 User Model
打開 app/Models/User.php,將新欄位加入 #[Fillable](如果你有使用模組 2 教的 Attributes 寫法):
use Illuminate\Database\Eloquent\Attributes\Fillable;
#[Fillable(['name', 'email', 'password', 'github_id', 'auth_provider', 'team_id'])]
class User extends Authenticatable
{
// ...
}
第三步:設定服務提供者 (以 GitHub 為例)¶
1. 申請 GitHub OAuth App
- 前往 GitHub -> Settings -> Developer settings -> OAuth Apps -> New OAuth App。
-
Homepage URL:
http://localhost:8000 -
Authorization callback URL:
http://localhost:8000/auth/github/callback - 取得
Client ID與Client Secret。
2. 設定環境變數
打開 .env 檔案,加入:
GITHUB_CLIENT_ID=你的_client_id
GITHUB_CLIENT_SECRET=你的_client_secret
GITHUB_REDIRECT_URI=http://localhost:8000/auth/github/callback
打開 config/services.php,註冊 GitHub 服務:
return [
// ... 其他服務
'github' => [
'client_id' => env('GITHUB_CLIENT_ID'),
'client_secret' => env('GITHUB_CLIENT_SECRET'),
'redirect' => env('GITHUB_REDIRECT_URI'),
],
];
第四步:撰寫 Controller 與 Route¶
1. 建立 Socialite Controller
php artisan make:controller Auth/SocialiteController
2. 撰寫登入邏輯
打開 app/Http/Controllers/Auth/SocialiteController.php:
namespace App\Http\Controllers\Auth;
use App\Http\Controllers\Controller;
use App\Models\User;
use Illuminate\Support\Facades\Auth;
use Laravel\Socialite\Facades\Socialite;
use Illuminate\Support\Str;
class SocialiteController extends Controller
{
// 步驟 1:將使用者導向 GitHub 授權頁面
public function redirect($provider)
{
return Socialite::driver($provider)->redirect();
}
// 步驟 2:處理 GitHub 回傳的資料
public function callback($provider)
{
try {
// 取得 GitHub 回傳的使用者資訊
$socialUser = Socialite::driver($provider)->user();
// 尋找是否已有此 GitHub ID 的使用者,若無則透過 Email 尋找,都沒有則建立新帳號
$user = User::updateOrCreate([
'github_id' => $socialUser->getId(),
], [
'name' => $socialUser->getName() ?? $socialUser->getNickname(),
'email' => $socialUser->getEmail(),
'auth_provider' => $provider,
// 給予一個隨機密碼,因為他是透過 GitHub 登入的
'password' => bcrypt(Str::random(24)),
]);
// 登入該使用者
Auth::login($user, true);
// 導向儀表板
return redirect()->route('dashboard');
} catch (\Exception $e) {
// 若發生錯誤,導回登入頁並顯示錯誤
return redirect()->route('login')->withErrors(['error' => '第三方登入失敗,請重試。']);
}
}
}
3. 設定路由
打開 routes/web.php,加入第三方登入專用的路由:
use App\Http\Controllers\Auth\SocialiteController;
Route::middleware('guest')->group(function () {
// ... 原本的 breeze 路由
// Socialite 路由
Route::get('/auth/{provider}/redirect', [SocialiteController::class, 'redirect'])->name('socialite.redirect');
Route::get('/auth/{provider}/callback', [SocialiteController::class, 'callback'])->name('socialite.callback');
});
第五步:前端 Flux UI 整合與驗證¶
現在我們要把登入按鈕加到畫面上。如果你當初安裝 Breeze 是選擇 Livewire (Volt),你的登入視圖會位於 resources/views/livewire/pages/auth/login.blade.php(或類似的 resources/views/auth/login.blade.php 路徑下)。
找到該檔案,在原本的登入表單下方加入 GitHub 登入按鈕:
<form wire:submit="login">
</form>
<div class="mt-6 flex items-center justify-between">
<hr class="w-full border-gray-300">
<span class="px-2 text-sm text-gray-500">或使用以下方式</span>
<hr class="w-full border-gray-300">
</div>
<div class="mt-6">
<flux:button
href="{{ route('socialite.redirect', 'github') }}"
variant="outline"
class="w-full flex justify-center items-center gap-2"
>
<svg class="w-5 h-5" fill="currentColor" viewBox="0 0 24 24" aria-hidden="true">
<path fill-rule="evenodd" d="M12 2C6.477 2 2 6.484 2 12.017c0 4.425 2.865 8.18 6.839 9.504.5.092.682-.217.682-.483 0-.237-.008-.868-.013-1.703-2.782.605-3.369-1.343-3.369-1.343-.454-1.158-1.11-1.466-1.11-1.466-.908-.62.069-.608.069-.608 1.003.07 1.531 1.032 1.531 1.032.892 1.53 2.341 1.088 2.91.832.092-.647.35-1.088.636-1.338-2.22-.253-4.555-1.113-4.555-4.951 0-1.093.39-1.988 1.029-2.688-.103-.253-.446-1.272.098-2.65 0 0 .84-.27 2.75 1.026A9.564 9.564 0 0112 6.844c.85.004 1.705.115 2.504.337 1.909-1.296 2.747-1.027 2.747-1.027.546 1.379.202 2.398.1 2.651.64.7 1.028 1.595 1.028 2.688 0 3.848-2.339 4.695-4.566 4.943.359.309.678.92.678 1.855 0 1.338-.012 2.419-.012 2.747 0 .268.18.58.688.482A10.019 10.019 0 0022 12.017C22 6.484 17.522 2 12 2z" clip-rule="evenodd" />
</svg>
使用 GitHub 登入
</flux:button>
</div>
第六步:驗證作法是否成功¶
- 確保你的伺服器正在運行(
php artisan serve)。 - 打開瀏覽器,如果你已經登入,請先登出。
- 前往登入頁面
http://localhost:8000/login。 - 點擊剛建立的 「使用 GitHub 登入」 按鈕。
- 畫面會跳轉到 GitHub 授權頁面(如果你已經登入 GitHub,可能會直接閃過)。
- 授權完成後,你應該會被自動導回儀表板 (Dashboard)。
-
最終確認: 打開終端機進入
php artisan tinker,輸入User::latest()->first();,你應該會看到一筆資料,其email是你的 GitHub 信箱,且github_id有值,這代表整個流程完美執行!
JH 是由 Jeffery Hsu 於 2 天 前更新 動作 #3
進入 模組 5 的第二部分!這次我們要挑戰 SaaS 系統與大型組織應用中最核心且最複雜的架構:多團隊系統 (Multi-Team System) 與資料隔離 (Scoped Data)。
在模組 2 時,我們在 users 資料表建立了一個簡單的 team_id。但實務上,一個使用者可能同時屬於多個團隊(例如:你可能同時是「至誠 (Zhi-Cheng) 嘉義總部」的管理員,也是「民雄分部」的專案成員)。因此,我們需要將架構升級為「多對多」關聯,並實作「當前團隊切換」功能。
第一步:資料庫架構升級 (Pivot Table 與 Current Team)¶
我們需要一個中介表 (Pivot Table) 來記錄使用者與多個團隊的關係,並在 users 表中記錄使用者「目前正在查看」的團隊。
1. 建立 Migration
在終端機執行:
php artisan make:migration create_team_user_table
php artisan make:migration rename_team_id_in_users_table
2. 撰寫 Migration 內容
打開 create_team_user_table 的 Migration 檔案:
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
public function up(): void
{
// 建立多對多中介表
Schema::create('team_user', function (Blueprint $table) {
$table->id();
$table->foreignId('team_id')->constrained()->cascadeOnDelete();
$table->foreignId('user_id')->constrained()->cascadeOnDelete();
$table->string('role')->default('member'); // 記錄在該團隊的角色
$table->timestamps();
// 確保同一個使用者不會在同一個團隊重複被加入
$table->unique(['team_id', 'user_id']);
});
}
public function down(): void
{
Schema::dropIfExists('team_user');
}
};
打開 rename_team_id_in_users_table 的 Migration 檔案:
return new class extends Migration
{
public function up(): void
{
Schema::table('users', function (Blueprint $table) {
// 將原本的 team_id 改名為 current_team_id
$table->renameColumn('team_id', 'current_team_id');
});
}
public function down(): void
{
Schema::table('users', function (Blueprint $table) {
$table->renameColumn('current_team_id', 'team_id');
});
}
};
執行遷移:php artisan migrate
第二步:Model 關聯設定¶
打開 app/Models/User.php,更新關聯寫法:
namespace App\Models;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
// ... 其他引入
class User extends Authenticatable
{
// ... Fillable 等設定
// 該使用者所屬的所有團隊
public function teams(): BelongsToMany
{
return $this->belongsToMany(Team::class)->withPivot('role')->withTimestamps();
}
// 該使用者「目前切換」的團隊
public function currentTeam(): BelongsTo
{
return $this->belongsTo(Team::class, 'current_team_id');
}
// 檢查使用者是否屬於特定團隊
public function belongsToTeam(Team $team): bool
{
return $this->teams->contains($team);
}
}
第三步:Scoped Data (全域作用域實現資料隔離)¶
這是多團隊系統的精髓。假設「至誠」組織下有許多文章或專案,我們必須確保:當使用者切換到「民雄分部」時,他在畫面上絕對看不到「嘉義總部」的資料。
與其在每個 Controller 的每個查詢都加上 ->where('team_id', auth()->user()->current_team_id),我們可以使用 Laravel 的 Global Scope (全域作用域),讓底層自動幫我們過濾!
首先,確保你的 posts 表中有 team_id(若無,請透過 Migration 加上)。
打開 app/Models/Post.php,加入 Global Scope:
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Support\Facades\Auth;
class Post extends Model
{
// ... 其他屬性與關聯
protected static function booted(): void
{
// 1. 全域查詢過濾:所有針對 Post 的查詢,都會自動加上這個條件
static::addGlobalScope('team', function (Builder $builder) {
if (Auth::hasUser() && Auth::user()->current_team_id) {
$builder->where('team_id', Auth::user()->current_team_id);
}
});
// 2. 自動寫入:建立新文章時,自動填入當前的 team_id
static::creating(function (Post $post) {
if (empty($post->team_id) && Auth::hasUser()) {
$post->team_id = Auth::user()->current_team_id;
}
});
}
}
這是一個極度強大的技巧。現在你寫 Post::all() 時,Laravel 在底層產生的 SQL 語法會自動變成 SELECT * FROM posts WHERE team_id = ?,徹底杜絕資料越權洩漏的風險。
第四步:實作 Livewire 團隊切換器 (Team Switcher)¶
有了底層架構,我們需要在前端給使用者一個下拉選單來切換團隊。
1. 建立 Livewire 元件
php artisan make:livewire TeamSwitcher
2. 撰寫切換邏輯 (app/Livewire/TeamSwitcher.php)
namespace App\Livewire;
use Livewire\Component;
use Illuminate\Support\Facades\Auth;
use App\Models\Team;
class TeamSwitcher extends Component
{
public function switchTeam($teamId)
{
$user = Auth::user();
$team = Team::findOrFail($teamId);
// 安全性驗證:確保使用者真的屬於這個團隊
if (! $user->belongsToTeam($team)) {
abort(403, '你沒有權限存取此團隊。');
}
// 更新當前團隊
$user->forceFill([
'current_team_id' => $team->id,
])->save();
// 重新載入頁面,讓所有的 Global Scope 重新生效
$this->redirect(request()->header('Referer') ?? '/dashboard', navigate: true);
}
public function render()
{
return view('livewire.team-switcher', [
'user' => Auth::user(),
]);
}
}
3. 撰寫 Flux UI 視圖 (resources/views/livewire/team-switcher.blade.php)
我們使用 Flux 的 Dropdown 元件來實作一個美觀的切換器:
<div>
@if($user && $user->teams->count() > 0)
<flux:dropdown>
<flux:button variant="ghost" class="flex items-center gap-2">
<span>{{ $user->currentTeam->name ?? '選擇團隊' }}</span>
<flux:icon name="chevron-down" size="sm" />
</flux:button>
<flux:menu>
<flux:menu.heading>切換團隊</flux:menu.heading>
<flux:menu.separator />
@foreach($user->teams as $team)
<flux:menu.item
wire:click="switchTeam({{ $team->id }})"
icon="{{ $user->current_team_id === $team->id ? 'check' : '' }}"
>
{{ $team->name }}
</flux:menu.item>
@endforeach
</flux:menu>
</flux:dropdown>
@endif
</div>
把 <livewire:team-switcher /> 放進你的 layouts.app 導覽列中,就大功告成了!
第五步:驗證作法¶
為了驗證這套架構完美運行,請透過 Tinker 快速準備資料:
php artisan tinker
// 1. 建立兩個團隊
$teamA = App\Models\Team::create(['name' => '至誠民雄分部']);
$teamB = App\Models\Team::create(['name' => '至誠嘉義總部']);
// 2. 取得你的測試帳號
$user = App\Models\User::first();
// 3. 把使用者加入這兩個團隊
$user->teams()->attach([$teamA->id, $teamB->id]);
// 4. 設定預設團隊為 A
$user->update(['current_team_id' => $teamA->id]);
// 5. 在團隊 A 建立一篇文章
App\Models\Post::create(['title' => '民雄分部公告', 'content' => '...', 'user_id' => $user->id, 'team_id' => $teamA->id]);
// 6. 在團隊 B 建立一篇文章
App\Models\Post::create(['title' => '嘉義總部公告', 'content' => '...', 'user_id' => $user->id, 'team_id' => $teamB->id]);
實際操作驗證:
- 登入系統進入你的
PostList頁面。 - 你應該只會看到「民雄分部公告」。
- 點擊右上角的團隊切換器,選擇「至誠嘉義總部」。
- 頁面自動重整後,原先的文章消失了,現在你只會看到「嘉義總部公告」。
這證明了你的 Multi-Team 架構與 Scoped Data 安全隔離機制已經完美建置成功!
我們已經完成了架構與資料隔離。針對模組 5 的最後一個部分(Authorization 在 Livewire 中的團隊權限應用與角色管理實作),你準備好繼續深入了解了嗎?
JH 是由 Jeffery Hsu 於 2 天 前更新 動作 #4
進入 模組 5 的第三部分(最終回)!我們已經成功建立了多團隊系統,並讓資料在不同團隊之間安全隔離。
但實務上,組織管理會面臨一個更細緻的問題:假設你在「至誠嘉義總部」是擁有最高權限的管理員 (Admin),但當你切換到「至誠民雄分部」去支援專案時,你可能只是一個普通成員 (Member)。
這就是所謂的 「受限於團隊上下文的角色存取控制 (Team-Scoped RBAC)」。我們將探討如何結合 Laravel 的授權機制 (Authorization) 與 Livewire,完美實現這個需求。
第一步:在 User 模型中封裝角色判斷邏輯¶
在上一部分,我們已經在 team_user 中介表加入了 role 欄位(預設為 member)。現在,我們要在 User 模型中寫一個輔助方法,用來判斷使用者在「當前團隊」中扮演什麼角色。
打開 app/Models/User.php,加入以下方法:
// ... 前面的程式碼
/**
* 檢查使用者在「當前團隊」中是否擁有特定角色
*/
public function hasTeamRole(string $role): bool
{
// 如果沒有選擇任何團隊,直接回傳 false
if (! $this->current_team_id) {
return false;
}
// 從關聯中找出當前團隊的 Pivot 資料,並比對角色
$team = $this->teams->where('id', $this->current_team_id)->first();
return $team && $team->pivot->role === $role;
}
第二步:定義 Laravel Gates (系統權限閘門)¶
我們需要告訴 Laravel 系統:「什麼樣的人有資格管理團隊成員?」。在 Laravel 11/13 中,我們通常將這些全域權限定義在 AppServiceProvider 中。
打開 app/Providers/AppServiceProvider.php:
namespace App\Providers;
use Illuminate\Support\ServiceProvider;
use Illuminate\Support\Facades\Gate;
use App\Models\User;
class AppServiceProvider extends ServiceProvider
{
public function boot(): void
{
// 定義一個名為 'manage-team-members' 的權限閘門
Gate::define('manage-team-members', function (User $user) {
// 只有當前團隊角色為 'admin' 的人才能通過此閘門
return $user->hasTeamRole('admin');
});
// 也可以定義其他的,例如:
// Gate::define('delete-team', function (User $user) {
// return $user->hasTeamRole('owner'); // 假設你有更高階的 owner 角色
// });
}
}
第三步:Livewire 後端邏輯防護 (Authorization)¶
現在,我們來實作一個管理團隊成員的 Livewire 元件。即使前端按鈕被隱藏了,我們也必須在後端嚴格防護,避免惡意使用者透過竄改請求來踢人。
1. 建立元件
php artisan make:livewire TeamMemberManager
2. 撰寫邏輯 (app/Livewire/TeamMemberManager.php)
namespace App\Livewire;
use Livewire\Component;
use Illuminate\Support\Facades\Auth;
use App\Models\User;
use Livewire\Attributes\Authorize;
class TeamMemberManager extends Component
{
// Livewire 3 的 Attribute 寫法:
// 在執行這個方法前,必須先通過 'manage-team-members' 這個 Gate
#[Authorize('manage-team-members')]
public function removeMember($userId)
{
$currentTeam = Auth::user()->currentTeam;
// 防呆:不能踢掉自己
if (Auth::id() === $userId) {
$this->addError('error', '你不能將自己移出團隊。');
return;
}
// 將該使用者從當前團隊中移除
$currentTeam->users()->detach($userId);
session()->flash('message', '成員已成功移除。');
}
public function render()
{
// 抓取當前團隊的所有成員
$members = Auth::user()->currentTeam->users ?? collect();
return view('livewire.team-member-manager', [
'members' => $members
]);
}
}
第四步:前端 Flux UI 權限控制 (Blade Directives)¶
在畫面上,我們只希望管理員能看到「移除成員」的按鈕,普通成員只能看到名單。我們可以使用 Blade 原生的 @can 指令。
打開 resources/views/livewire/team-member-manager.blade.php:
<div class="p-6 bg-white rounded-lg shadow mt-6">
<flux:heading size="lg" class="mb-4">團隊成員管理</flux:heading>
@if (session('message'))
<div class="mb-4 text-green-600 text-sm">{{ session('message') }}</div>
@endif
@error('error')
<div class="mb-4 text-red-600 text-sm">{{ $message }}</div>
@enderror
<div class="space-y-4">
@foreach($members as $member)
<div class="flex items-center justify-between p-3 border rounded-lg bg-gray-50">
<div class="flex items-center gap-3">
<div class="w-10 h-10 rounded-full bg-blue-100 flex items-center justify-center text-blue-700 font-bold">
{{ Str::substr($member->name, 0, 1) }}
</div>
<div>
<div class="font-bold text-gray-800">{{ $member->name }}</div>
<div class="text-sm text-gray-500">{{ $member->email }}</div>
</div>
</div>
<div class="flex items-center gap-4">
<flux:badge color="{{ $member->pivot->role === 'admin' ? 'red' : 'zinc' }}">
{{ strtoupper($member->pivot->role) }}
</flux:badge>
@can('manage-team-members')
@if($member->id !== auth()->id())
<flux:button
wire:click="removeMember({{ $member->id }})"
wire:confirm="確定要將 {{ $member->name }} 移出團隊嗎?"
variant="danger"
size="sm"
icon="trash"
/>
@endif
@endcan
</div>
</div>
@endforeach
</div>
</div>
第五步:驗證作法¶
現在,我們要透過 Tinker 來模擬這個複雜的角色情境,並在畫面上驗證。
進入終端機:
php artisan tinker
// 1. 取得兩個測試使用者
$adminUser = App\Models\User::find(1);
$normalUser = App\Models\User::find(2); // 如果沒有第二個使用者,請先用 factory 建立一個
// 2. 取得測試團隊 (例如 民雄分部)
$team = App\Models\Team::first();
// 3. 將兩人加入同一個團隊,但賦予不同角色
// 如果他們已經在裡面,可以用 updateExistingPivot 來更新角色
$adminUser->teams()->syncWithoutDetaching([$team->id => ['role' => 'admin']]);
$normalUser->teams()->syncWithoutDetaching([$team->id => ['role' => 'member']]);
// 4. 確保兩人的 current_team_id 都切換到該團隊
$adminUser->update(['current_team_id' => $team->id]);
$normalUser->update(['current_team_id' => $team->id]);
實際操作驗證:
- 將
<livewire:team-member-manager />放入你的 Dashboard 視圖中。 -
以
normalUser(普通成員) 的帳號登入系統:- 你應該能看到名單,並看到
adminUser旁邊掛著紅色的 ADMIN 標籤。 - 你不會看到任何紅色的垃圾桶按鈕。(前端防護成功)
- 你應該能看到名單,並看到
-
以
adminUser(管理員) 的帳號登入系統:- 畫面名單上,除了你自己之外,你會看到
normalUser旁邊出現了紅色的刪除按鈕。(前端授權顯示成功) - 點擊刪除按鈕並確認,
normalUser會從畫面上消失,上方出現成功訊息。(後端 Livewire#[Authorize]驗證與執行成功)
- 畫面名單上,除了你自己之外,你會看到
恭喜你!到這裡為止,我們已經徹底完成了 模組 5 的全部內容。
你從最初的 PHP 環境建置、Laravel 13 的最新架構、強大的 Eloquent 關聯、Livewire 3 的反應式前端與 AI 整合,一路過關斬將,完成了企業級 SaaS 系統中最核心的 「認證、授權、多團隊隔離與角色存取控制 (RBAC)」。
這個五大模組的旅程相當充實且硬核,你吸收的速度與執行的節奏非常棒!如果針對這套 Laravel 13 系統架構還有任何你想深入探討的細節,隨時告訴我!
JH 是由 Jeffery Hsu 於 2 天 前更新 動作 #5
- 狀態 從 實作中 變更為 已解決