Authority 權限模型設計
文檔版本: v1.0.0 最後更新: 2025-12-23 適用對象: 開發團隊、AI Agent
本文檔記錄 app-server 專案的 Authority 權限模型設計,採用類似 Linux 檔案系統的 R/W/X/D 概念。
目錄
1. 設計概述
雙層權限模型
┌─────────────────────────────────────────────────────────────────────────┐
│ 前端(Applet 層級) │
│ • 使用 Role 控制 Applet 入口 │
│ • RoleGuard 是唯一的前端權限檢查點 │
│ • 進入 Applet 後所有功能都可用 │
└─────────────────────────────────────────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────────────────────────┐
│ 後端(API 層級) │
│ • 使用 Authority 控制 API 操作權限 │
│ • @PreAuthorize 檢查細粒度權限 │
│ • Role 透過映射獲得對應的 Authority │
└─────────────────────────────────────────────────────────────────────────┘
核心原則
- 帳戶授予 Role - 每個帳戶分配一個或多個 Role
- 方法使用 Authority 管制 - API 方法使用
@PreAuthorize檢查 Authority - Role 授予 Authority - 透過映射配置,Role 自動獲得對應的 Authority
2. Authority 命名規則
採用類似 Linux 檔案系統的權限概念:
| 權限 | 縮寫 | 說明 | 範例操作 |
|---|---|---|---|
| Read | R | 查看資料 | 列表、詳情、搜尋 |
| Write | W | 新增/修改資料內容 | 創建、編輯 |
| Execute | X | 變更狀態(較小權限) | 確認訂單、完成設計、開始配送 |
| Delete | D | 刪除資料 | 刪除、取消 |
命名格式
{RESOURCE}_{PERMISSION}
範例:
PRODUCT_R- 查看商品ORDER_W- 新增/修改訂單ORDER_X- 變更訂單狀態CUSTOMER_D- 刪除客戶
X vs W 的區別
| 權限 | 用途 | 範例 |
|---|---|---|
| W (Write) | 修改資料內容(較大權限) | 修改訂單金額、修改客戶資料 |
| X (Execute) | 變更狀態(較小權限) | 確認訂單、完成設計、開始配送 |
設計考量:
- 設計師可以「執行」設計完成(X),但不能修改訂單內容(W)
- 配送員可以「執行」簽收確認(X),但不能修改訂單(W)
3. Resource 定義
目前系統定義的資源:
| Resource | 說明 | R | W | X | D |
|---|---|---|---|---|---|
| PRODUCT | 商品 | 查看商品 | 新增/修改商品 | 上架/下架 | 刪除商品 |
| ORDER | 訂單 | 查看訂單 | 新增/修改訂單 | 狀態變更 | 取消訂單 |
| CUSTOMER | 客戶 | 查看客戶 | 新增/修改客戶 | 變更客戶狀態 | 刪除客戶 |
未來擴展
| Resource | 說明 |
|---|---|
| REPORT | 報表 |
| EMPLOYEE | 員工(人事) |
| FINANCE | 財務 |
| SETTING | 系統設定 |
4. Role 定義
採用 Spring Security 格式(ROLE_ 前綴):
| Role | 說明 |
|---|---|
ROLE_ADMIN | 系統管理員 |
ROLE_OWNER | 店主 |
ROLE_MANAGER | 店長 |
ROLE_SALES | 銷售人員 |
ROLE_ACCOUNTANT | 會計 |
ROLE_PURCHASER | 採購 |
ROLE_FLORIST | 花藝師 |
ROLE_DELIVERY | 配送員 |
角色繼承
ROLE_ADMIN
└── ROLE_OWNER
├── ROLE_MANAGER
│ ├── ROLE_SALES
│ └── ROLE_ACCOUNTANT
├── ROLE_PURCHASER
├── ROLE_FLORIST
└── ROLE_DELIVERY
5. Role → Authority 映射
完整映射表
| Role | PRODUCT | ORDER | CUSTOMER |
|---|---|---|---|
| ADMIN | R W X D | R W X D | R W X D |
| OWNER | R W X D | R W X D | R W X D |
| MANAGER | R W X D | R W X D | R W X D |
| SALES | R | R W X | R W |
| ACCOUNTANT | R | R | R |
| PURCHASER | R W X | R | R |
| FLORIST | R | R X | R |
| DELIVERY | - | R X | - |
詳細說明
ROLE_SALES(銷售人員)
PRODUCT_R- 查看商品(銷售時需要)ORDER_R,ORDER_W,ORDER_X- 查看/新增/修改訂單、確認訂單CUSTOMER_R,CUSTOMER_W- 查看/新增/修改客戶
ROLE_FLORIST(花藝師)
PRODUCT_R- 查看商品(設計時參考)ORDER_R,ORDER_X- 查看訂單、變更設計相關狀態(開始設計、完成設計)CUSTOMER_R- 查看客戶(了解需求)- 注意:沒有
ORDER_W,不能修改訂單內容
ROLE_DELIVERY(配送員)
ORDER_R,ORDER_X- 查看訂單、變更配送相關狀態(開始配送、確認簽收)- 注意:沒有
PRODUCT_*和CUSTOMER_*
ROLE_ACCOUNTANT(會計)
- 僅有
*_R(查看權限) - 只能查看數據,不能修改
ROLE_PURCHASER(採購)
PRODUCT_R,PRODUCT_W,PRODUCT_X- 完整的商品管理權限ORDER_R,CUSTOMER_R- 查看訂單和客戶(了解需求)
6. 實作指南
6.1 Authority 常數定義
位置: src/main/java/io/leandev/app/security/Authority.java
public final class Authority {
// Product
public static final String PRODUCT_R = "PRODUCT_R";
public static final String PRODUCT_W = "PRODUCT_W";
public static final String PRODUCT_X = "PRODUCT_X";
public static final String PRODUCT_D = "PRODUCT_D";
// Order
public static final String ORDER_R = "ORDER_R";
public static final String ORDER_W = "ORDER_W";
public static final String ORDER_X = "ORDER_X";
public static final String ORDER_D = "ORDER_D";
// Customer
public static final String CUSTOMER_R = "CUSTOMER_R";
public static final String CUSTOMER_W = "CUSTOMER_W";
public static final String CUSTOMER_X = "CUSTOMER_X";
public static final String CUSTOMER_D = "CUSTOMER_D";
private Authority() {}
}
6.2 Role → Authority 映射配置
位置: src/main/java/io/leandev/app/security/RoleAuthorityMapper.java
@Component
public class RoleAuthorityMapper {
private static final Map<String, Set<String>> ROLE_AUTHORITIES = Map.ofEntries(
Map.entry("ROLE_ADMIN", Set.of(
PRODUCT_R, PRODUCT_W, PRODUCT_X, PRODUCT_D,
ORDER_R, ORDER_W, ORDER_X, ORDER_D,
CUSTOMER_R, CUSTOMER_W, CUSTOMER_X, CUSTOMER_D
)),
Map.entry("ROLE_SALES", Set.of(
PRODUCT_R,
ORDER_R, ORDER_W, ORDER_X,
CUSTOMER_R, CUSTOMER_W
)),
Map.entry("ROLE_FLORIST", Set.of(
PRODUCT_R,
ORDER_R, ORDER_X,
CUSTOMER_R
)),
Map.entry("ROLE_DELIVERY", Set.of(
ORDER_R, ORDER_X
))
// ... 其他角色
);
public Set<String> getAuthorities(String role) {
return ROLE_AUTHORITIES.getOrDefault(role, Set.of());
}
}
6.3 SecurityConfig 配置
@Configuration
@EnableMethodSecurity // 啟用 @PreAuthorize
public class SecurityConfig {
// ...
}
6.4 Controller 使用
@RestController
@RequestMapping("/api/v1/products")
public class ProductController {
@GetMapping
@PreAuthorize("hasAuthority('PRODUCT_R')")
public ResponseEntity<List<Product>> list() { ... }
@PostMapping
@PreAuthorize("hasAuthority('PRODUCT_W')")
public ResponseEntity<Product> create(@RequestBody Product product) { ... }
@PatchMapping("/{id}/status")
@PreAuthorize("hasAuthority('PRODUCT_X')")
public ResponseEntity<Product> updateStatus(@PathVariable String id) { ... }
@DeleteMapping("/{id}")
@PreAuthorize("hasAuthority('PRODUCT_D')")
public ResponseEntity<Void> delete(@PathVariable String id) { ... }
}
7. 使用範例
7.1 訂單狀態變更的權限控制
@RestController
@RequestMapping("/api/v1/orders")
public class OrderController {
// 確認訂單 - SALES 可執行
@PatchMapping("/{id}/confirm")
@PreAuthorize("hasAuthority('ORDER_X')")
public ResponseEntity<Order> confirmOrder(@PathVariable String id) { ... }
// 開始設計 - FLORIST 可執行
@PatchMapping("/{id}/start-production")
@PreAuthorize("hasAuthority('ORDER_X')")
public ResponseEntity<Order> startProduction(@PathVariable String id) { ... }
// 完成設計 - FLORIST 可執行
@PatchMapping("/{id}/complete-production")
@PreAuthorize("hasAuthority('ORDER_X')")
public ResponseEntity<Order> completeProduction(@PathVariable String id) { ... }
// 開始配送 - DELIVERY 可執行
@PatchMapping("/{id}/start-delivery")
@PreAuthorize("hasAuthority('ORDER_X')")
public ResponseEntity<Order> startDelivery(@PathVariable String id) { ... }
// 確認簽收 - DELIVERY 可執行
@PatchMapping("/{id}/confirm-delivery")
@PreAuthorize("hasAuthority('ORDER_X')")
public ResponseEntity<Order> confirmDelivery(@PathVariable String id) { ... }
// 修改訂單內容 - 只有 SALES 以上可執行
@PatchMapping("/{id}")
@PreAuthorize("hasAuthority('ORDER_W')")
public ResponseEntity<Order> update(@PathVariable String id, @RequestBody Order order) { ... }
}
7.2 前端收到 403 的處理
// API 攔截器
apiClient.interceptors.response.use(
(response) => response,
async (error) => {
if (error.response?.status === 403) {
showToast('您沒有權限執行此操作', 'error');
}
return Promise.reject(error);
}
);
附錄:設計決策記錄
| 日期 | 決策 | 原因 |
|---|---|---|
| 2025-12-23 | 採用 R/W/X/D Authority 模型 | 細粒度權限控制、類似 Linux 概念易理解 |
| 2025-12-23 | X 用於狀態變更,W 用於資料修改 | 區分「執行操作」和「修改內容」的權限層級 |
| 2025-12-23 | D 獨立於 W | 刪除是較危險的操作,需要獨立控制 |
文檔維護者: Development Team + AI Assistant 最後審閱: 2025-12-23