141 lines
4.0 KiB
Markdown
141 lines
4.0 KiB
Markdown
|
|
---
|
|||
|
|
name: 權限管理與實作規範
|
|||
|
|
description: 為新功能實作權限控制的完整流程規範,包含後端 Seeder 設定、Middleware 路由保護與前端權限判斷。
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
# 權限管理與實作規範
|
|||
|
|
|
|||
|
|
本文件說明如何在新增功能時,一併實作完整的權限控制機制。專案採用 `spatie/laravel-permission` 套件進行權限管理。
|
|||
|
|
|
|||
|
|
## 1. 定義權限 (Backend)
|
|||
|
|
|
|||
|
|
所有權限皆定義於 `database/seeders/PermissionSeeder.php`。
|
|||
|
|
|
|||
|
|
### 步驟:
|
|||
|
|
|
|||
|
|
1. 開啟 `database/seeders/PermissionSeeder.php`。
|
|||
|
|
2. 在 `$permissions` 陣列中新增功能對應的權限字串。
|
|||
|
|
* **命名慣例**:`{resource}.{action}` (例如:`system.view_logs`, `products.create`)
|
|||
|
|
* 常用動作:`view`, `create`, `edit`, `delete`, `publish`, `export`
|
|||
|
|
3. 在下方「角色分配」區段,將新權限分配給適合的角色。
|
|||
|
|
* `super-admin`:通常擁有所有權限(程式碼中 `Permission::all()` 自動涵蓋,無需手動新增)。
|
|||
|
|
* `admin`:通常擁有大部分權限。
|
|||
|
|
* 其他角色 (`warehouse-manager`, `purchaser`, `viewer`):依業務邏輯分配。
|
|||
|
|
|
|||
|
|
### 範例:
|
|||
|
|
|
|||
|
|
```php
|
|||
|
|
// 1. 新增權限字串
|
|||
|
|
$permissions = [
|
|||
|
|
// ... 現有權限
|
|||
|
|
'system.view_logs', // 新增:檢視系統日誌
|
|||
|
|
];
|
|||
|
|
|
|||
|
|
// ...
|
|||
|
|
|
|||
|
|
// 2. 分配給角色
|
|||
|
|
$admin->givePermissionTo([
|
|||
|
|
// ... 現有權限
|
|||
|
|
'system.view_logs',
|
|||
|
|
]);
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
## 2. 套用資料庫變更
|
|||
|
|
|
|||
|
|
修改 Seeder 後,必須重新執行 Seeder 以將權限寫入資料庫。
|
|||
|
|
|
|||
|
|
```bash
|
|||
|
|
# 對於所有租戶執行 Seeder (開發環境)
|
|||
|
|
php artisan tenants:seed --class=PermissionSeeder
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
## 3. 路由保護 (Backend Middleware)
|
|||
|
|
|
|||
|
|
在 `routes/web.php` 中,使用 `permission:{name}` middleware 保護路由。
|
|||
|
|
|
|||
|
|
### 範例:
|
|||
|
|
|
|||
|
|
```php
|
|||
|
|
// 單一權限保護
|
|||
|
|
Route::get('/logs', [LogController::class, 'index'])
|
|||
|
|
->middleware('permission:system.view_logs')
|
|||
|
|
->name('logs.index');
|
|||
|
|
|
|||
|
|
// 路由群組保護
|
|||
|
|
Route::middleware('permission:products.view')->group(function () {
|
|||
|
|
// ...
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
// 多重權限 (OR 邏輯:有其一即可)
|
|||
|
|
Route::middleware('permission:products.create|products.edit')->group(function () {
|
|||
|
|
// ...
|
|||
|
|
});
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
## 4. 前端權限判斷 (React Component)
|
|||
|
|
|
|||
|
|
使用自訂 Hook `usePermission` 來控制 UI 元素的顯示(例如:隱藏沒有權限的按鈕)。
|
|||
|
|
|
|||
|
|
### 引入 Hook:
|
|||
|
|
|
|||
|
|
```tsx
|
|||
|
|
import { usePermission } from "@/hooks/usePermission";
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 使用方式:
|
|||
|
|
|
|||
|
|
```tsx
|
|||
|
|
export default function ProductIndex() {
|
|||
|
|
const { can } = usePermission();
|
|||
|
|
|
|||
|
|
return (
|
|||
|
|
<div>
|
|||
|
|
<h1>商品列表</h1>
|
|||
|
|
|
|||
|
|
{/* 只有擁有 create 權限才顯示按鈕 */}
|
|||
|
|
{can('products.create') && (
|
|||
|
|
<Button>新增商品</Button>
|
|||
|
|
)}
|
|||
|
|
|
|||
|
|
{/* 組合判斷 */}
|
|||
|
|
{can('products.edit') && <EditButton />}
|
|||
|
|
</div>
|
|||
|
|
);
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 權限 Hook 介面說明:
|
|||
|
|
|
|||
|
|
- `can(permission: string)`: 檢查當前使用者是否擁有指定權限。
|
|||
|
|
- `canAny(permissions: string[])`: 檢查當前使用者是否擁有陣列中**任一**權限。
|
|||
|
|
- `hasRole(role: string)`: 檢查當前使用者是否擁有指定角色。
|
|||
|
|
|
|||
|
|
## 5. 配置權限群組名稱 (Backend UI Config)
|
|||
|
|
|
|||
|
|
為了讓新權限在「角色與權限」管理介面中顯示正確的中文分組標題,需修改 Controller 設定。
|
|||
|
|
|
|||
|
|
### 步驟:
|
|||
|
|
|
|||
|
|
1. 開啟 `app/Http/Controllers/Admin/RoleController.php`。
|
|||
|
|
2. 找到 `getGroupedPermissions` 方法。
|
|||
|
|
3. 在 `$groupDefinitions` 陣列中,新增 `{resource}` 對應的中文名稱。
|
|||
|
|
|
|||
|
|
### 範例:
|
|||
|
|
|
|||
|
|
```php
|
|||
|
|
$groupDefinitions = [
|
|||
|
|
'products' => '商品資料管理',
|
|||
|
|
// ...
|
|||
|
|
'utility_fees' => '公共事業費管理', // 新增此行
|
|||
|
|
];
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
## 檢核清單
|
|||
|
|
|
|||
|
|
- [ ] `PermissionSeeder.php` 已新增權限字串。
|
|||
|
|
- [ ] `PermissionSeeder.php` 已將新權限分配給對應角色。
|
|||
|
|
- [ ] 已執行 `php artisan tenants:seed --class=PermissionSeeder` 更新資料庫。
|
|||
|
|
- [ ] `RoleController.php` 已新增權限群組的中文名稱映射。
|
|||
|
|
- [ ] 後端路由 (`routes/web.php`) 已加上 middleware 保護。
|
|||
|
|
- [ ] 前端頁面/按鈕已使用 `usePermission` 進行顯示控制。
|