515 lines
12 KiB
Markdown
515 lines
12 KiB
Markdown
# 使用範例
|
|
|
|
## 完整範例:從零到完整權限系統
|
|
|
|
### 1. 初始化系統
|
|
|
|
```go
|
|
package main
|
|
|
|
import (
|
|
"context"
|
|
"log"
|
|
|
|
"permission/reborn/config"
|
|
"permission/reborn/repository"
|
|
"permission/reborn/usecase"
|
|
|
|
"github.com/go-redis/redis/v8"
|
|
"gorm.io/driver/mysql"
|
|
"gorm.io/gorm"
|
|
)
|
|
|
|
func main() {
|
|
// 1. 載入配置
|
|
cfg := config.ExampleConfig()
|
|
|
|
// 2. 初始化資料庫
|
|
dsn := fmt.Sprintf("%s:%s@tcp(%s:%d)/%s?charset=utf8mb4&parseTime=True&loc=Local",
|
|
cfg.Database.Username,
|
|
cfg.Database.Password,
|
|
cfg.Database.Host,
|
|
cfg.Database.Port,
|
|
cfg.Database.Database,
|
|
)
|
|
|
|
db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
|
|
// 3. 初始化 Redis
|
|
redisClient := redis.NewClient(&redis.Options{
|
|
Addr: fmt.Sprintf("%s:%d", cfg.Redis.Host, cfg.Redis.Port),
|
|
Password: cfg.Redis.Password,
|
|
DB: cfg.Redis.DB,
|
|
})
|
|
|
|
// 4. 建立 Repository 層
|
|
roleRepo := repository.NewRoleRepository(db)
|
|
permRepo := repository.NewPermissionRepository(db, nil) // 先不用快取
|
|
rolePermRepo := repository.NewRolePermissionRepository(db)
|
|
userRoleRepo := repository.NewUserRoleRepository(db)
|
|
cacheRepo := repository.NewCacheRepository(redisClient, cfg.Redis)
|
|
|
|
// 更新 permRepo 加入快取
|
|
permRepo = repository.NewPermissionRepository(db, cacheRepo)
|
|
|
|
// 5. 建立 UseCase 層
|
|
permUseCase := usecase.NewPermissionUseCase(
|
|
permRepo, rolePermRepo, roleRepo, userRoleRepo, cacheRepo,
|
|
)
|
|
|
|
rolePermUseCase := usecase.NewRolePermissionUseCase(
|
|
permRepo, rolePermRepo, roleRepo, userRoleRepo,
|
|
permUseCase, cacheRepo, cfg.Role,
|
|
)
|
|
|
|
roleUseCase := usecase.NewRoleUseCase(
|
|
roleRepo, userRoleRepo, rolePermUseCase, cacheRepo, cfg.Role,
|
|
)
|
|
|
|
userRoleUseCase := usecase.NewUserRoleUseCase(
|
|
userRoleRepo, roleRepo, cacheRepo,
|
|
)
|
|
|
|
// 6. 開始使用
|
|
ctx := context.Background()
|
|
|
|
// 範例使用
|
|
runExamples(ctx, roleUseCase, userRoleUseCase, rolePermUseCase, permUseCase)
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
### 2. 建立角色和權限
|
|
|
|
```go
|
|
func runExamples(ctx context.Context,
|
|
roleUC usecase.RoleUseCase,
|
|
userRoleUC usecase.UserRoleUseCase,
|
|
rolePermUC usecase.RolePermissionUseCase,
|
|
permUC usecase.PermissionUseCase,
|
|
) {
|
|
// 假設資料庫已經有以下權限
|
|
// - user (ID: 1, ParentID: 0)
|
|
// - user.list (ID: 2, ParentID: 1)
|
|
// - user.create (ID: 3, ParentID: 1)
|
|
// - user.update (ID: 4, ParentID: 1)
|
|
// - user.delete (ID: 5, ParentID: 1)
|
|
|
|
// 建立「使用者管理員」角色
|
|
adminRole, err := roleUC.Create(ctx, usecase.CreateRoleRequest{
|
|
ClientID: 1,
|
|
Name: "使用者管理員",
|
|
Permissions: entity.Permissions{
|
|
"user.list": entity.PermissionOpen,
|
|
"user.create": entity.PermissionOpen,
|
|
"user.update": entity.PermissionOpen,
|
|
"user.delete": entity.PermissionOpen,
|
|
},
|
|
})
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
|
|
fmt.Printf("建立角色成功: %s (%s)\n", adminRole.Name, adminRole.UID)
|
|
|
|
// 建立「使用者檢視者」角色
|
|
viewerRole, err := roleUC.Create(ctx, usecase.CreateRoleRequest{
|
|
ClientID: 1,
|
|
Name: "使用者檢視者",
|
|
Permissions: entity.Permissions{
|
|
"user.list": entity.PermissionOpen,
|
|
},
|
|
})
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
|
|
fmt.Printf("建立角色成功: %s (%s)\n", viewerRole.Name, viewerRole.UID)
|
|
}
|
|
```
|
|
|
|
輸出:
|
|
```
|
|
建立角色成功: 使用者管理員 (RL000001)
|
|
建立角色成功: 使用者檢視者 (RL000002)
|
|
```
|
|
|
|
---
|
|
|
|
### 3. 指派角色給使用者
|
|
|
|
```go
|
|
// 指派「使用者管理員」角色給使用者 Alice
|
|
aliceRole, err := userRoleUC.Assign(ctx, usecase.AssignRoleRequest{
|
|
UserUID: "U000001",
|
|
RoleUID: adminRole.UID,
|
|
Brand: "default",
|
|
})
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
|
|
fmt.Printf("使用者 %s 被指派角色: %s\n", aliceRole.UserUID, aliceRole.RoleUID)
|
|
|
|
// 指派「使用者檢視者」角色給使用者 Bob
|
|
bobRole, err := userRoleUC.Assign(ctx, usecase.AssignRoleRequest{
|
|
UserUID: "U000002",
|
|
RoleUID: viewerRole.UID,
|
|
Brand: "default",
|
|
})
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
|
|
fmt.Printf("使用者 %s 被指派角色: %s\n", bobRole.UserUID, bobRole.RoleUID)
|
|
```
|
|
|
|
輸出:
|
|
```
|
|
使用者 U000001 被指派角色: RL000001
|
|
使用者 U000002 被指派角色: RL000002
|
|
```
|
|
|
|
---
|
|
|
|
### 4. 查詢使用者權限
|
|
|
|
```go
|
|
// 查詢 Alice 的完整權限
|
|
alicePerm, err := rolePermUC.GetByUserUID(ctx, "U000001")
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
|
|
fmt.Printf("\n使用者 %s 的權限:\n", alicePerm.UserUID)
|
|
fmt.Printf("角色: %s (%s)\n", alicePerm.RoleName, alicePerm.RoleUID)
|
|
fmt.Printf("權限列表:\n")
|
|
for name, status := range alicePerm.Permissions {
|
|
fmt.Printf(" - %s: %s\n", name, status)
|
|
}
|
|
```
|
|
|
|
輸出:
|
|
```
|
|
使用者 U000001 的權限:
|
|
角色: 使用者管理員 (RL000001)
|
|
權限列表:
|
|
- user: open (父權限自動展開)
|
|
- user.list: open
|
|
- user.create: open
|
|
- user.update: open
|
|
- user.delete: open
|
|
```
|
|
|
|
---
|
|
|
|
### 5. 檢查 API 權限
|
|
|
|
```go
|
|
// Alice 嘗試存取 GET /api/users
|
|
checkResult, err := rolePermUC.CheckPermission(
|
|
ctx,
|
|
alicePerm.RoleUID,
|
|
"/api/users",
|
|
"GET",
|
|
)
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
|
|
fmt.Printf("\nAlice 存取 GET /api/users: ")
|
|
if checkResult.Allowed {
|
|
fmt.Printf("✅ 允許\n")
|
|
} else {
|
|
fmt.Printf("❌ 拒絕\n")
|
|
}
|
|
|
|
// Bob 嘗試刪除使用者 DELETE /api/users/123
|
|
bobPerm, _ := rolePermUC.GetByUserUID(ctx, "U000002")
|
|
checkResult, err = rolePermUC.CheckPermission(
|
|
ctx,
|
|
bobPerm.RoleUID,
|
|
"/api/users/123",
|
|
"DELETE",
|
|
)
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
|
|
fmt.Printf("Bob 存取 DELETE /api/users/123: ")
|
|
if checkResult.Allowed {
|
|
fmt.Printf("✅ 允許\n")
|
|
} else {
|
|
fmt.Printf("❌ 拒絕 (原因: 沒有 user.delete 權限)\n")
|
|
}
|
|
```
|
|
|
|
輸出:
|
|
```
|
|
Alice 存取 GET /api/users: ✅ 允許
|
|
Bob 存取 DELETE /api/users/123: ❌ 拒絕 (原因: 沒有 user.delete 權限)
|
|
```
|
|
|
|
---
|
|
|
|
### 6. 更新角色權限
|
|
|
|
```go
|
|
// 升級 Bob 的角色權限,加入 user.create
|
|
err = rolePermUC.UpdateRolePermissions(ctx, viewerRole.UID, entity.Permissions{
|
|
"user.list": entity.PermissionOpen,
|
|
"user.create": entity.PermissionOpen, // 新增
|
|
})
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
|
|
fmt.Printf("\n角色 %s 權限已更新\n", viewerRole.UID)
|
|
|
|
// 重新查詢 Bob 的權限 (快取會自動失效)
|
|
bobPerm, err = rolePermUC.GetByUserUID(ctx, "U000002")
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
|
|
fmt.Printf("Bob 的新權限:\n")
|
|
for name, status := range bobPerm.Permissions {
|
|
fmt.Printf(" - %s: %s\n", name, status)
|
|
}
|
|
```
|
|
|
|
輸出:
|
|
```
|
|
角色 RL000002 權限已更新
|
|
Bob 的新權限:
|
|
- user: open
|
|
- user.list: open
|
|
- user.create: open (新增)
|
|
```
|
|
|
|
---
|
|
|
|
### 7. 查詢角色列表 (含使用者數量)
|
|
|
|
```go
|
|
// 分頁查詢所有角色
|
|
pageResp, err := roleUC.Page(ctx, usecase.RoleFilterRequest{
|
|
ClientID: 1,
|
|
}, 1, 10)
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
|
|
fmt.Printf("\n角色列表 (共 %d 個):\n", pageResp.Total)
|
|
for _, role := range pageResp.List {
|
|
fmt.Printf(" - %s (%s) - %d 個使用者\n",
|
|
role.Name, role.UID, role.UserCount)
|
|
}
|
|
```
|
|
|
|
輸出:
|
|
```
|
|
角色列表 (共 2 個):
|
|
- 使用者管理員 (RL000001) - 1 個使用者
|
|
- 使用者檢視者 (RL000002) - 1 個使用者
|
|
```
|
|
|
|
---
|
|
|
|
### 8. 查詢擁有特定權限的使用者
|
|
|
|
```go
|
|
// 查詢所有有 user.delete 權限的使用者
|
|
userUIDs, err := permUC.GetUsersByPermission(ctx, []string{"user.delete"})
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
|
|
fmt.Printf("\n擁有 user.delete 權限的使用者:\n")
|
|
for _, uid := range userUIDs {
|
|
fmt.Printf(" - %s\n", uid)
|
|
}
|
|
```
|
|
|
|
輸出:
|
|
```
|
|
擁有 user.delete 權限的使用者:
|
|
- U000001
|
|
```
|
|
|
|
---
|
|
|
|
### 9. 取得權限樹
|
|
|
|
```go
|
|
tree, err := permUC.GetTree(ctx)
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
|
|
fmt.Printf("\n權限樹結構:\n")
|
|
printTree(tree, 0)
|
|
|
|
func printTree(node *usecase.PermissionTreeNode, indent int) {
|
|
prefix := strings.Repeat(" ", indent)
|
|
fmt.Printf("%s- %s\n", prefix, node.Name)
|
|
for _, child := range node.Children {
|
|
printTree(child, indent+1)
|
|
}
|
|
}
|
|
```
|
|
|
|
輸出:
|
|
```
|
|
權限樹結構:
|
|
- user
|
|
- user.list
|
|
- user.create
|
|
- user.update
|
|
- user.delete
|
|
```
|
|
|
|
---
|
|
|
|
### 10. 完整的 HTTP Handler 範例
|
|
|
|
```go
|
|
package handler
|
|
|
|
import (
|
|
"net/http"
|
|
|
|
"github.com/gin-gonic/gin"
|
|
"permission/reborn/domain/usecase"
|
|
)
|
|
|
|
type PermissionHandler struct {
|
|
rolePermUC usecase.RolePermissionUseCase
|
|
}
|
|
|
|
// CheckPermissionMiddleware 權限檢查中間件
|
|
func (h *PermissionHandler) CheckPermissionMiddleware() gin.HandlerFunc {
|
|
return func(c *gin.Context) {
|
|
// 從 JWT 或 Session 取得使用者資訊
|
|
userUID := c.GetString("user_uid")
|
|
if userUID == "" {
|
|
c.JSON(http.StatusUnauthorized, gin.H{"error": "unauthorized"})
|
|
c.Abort()
|
|
return
|
|
}
|
|
|
|
// 取得使用者權限
|
|
userPerm, err := h.rolePermUC.GetByUserUID(c.Request.Context(), userUID)
|
|
if err != nil {
|
|
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
|
c.Abort()
|
|
return
|
|
}
|
|
|
|
// 檢查權限
|
|
checkResult, err := h.rolePermUC.CheckPermission(
|
|
c.Request.Context(),
|
|
userPerm.RoleUID,
|
|
c.Request.URL.Path,
|
|
c.Request.Method,
|
|
)
|
|
if err != nil {
|
|
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
|
c.Abort()
|
|
return
|
|
}
|
|
|
|
if !checkResult.Allowed {
|
|
c.JSON(http.StatusForbidden, gin.H{
|
|
"error": "permission denied",
|
|
"required_permission": checkResult.PermissionName,
|
|
})
|
|
c.Abort()
|
|
return
|
|
}
|
|
|
|
// 將權限資訊存入 context
|
|
c.Set("role_uid", userPerm.RoleUID)
|
|
c.Set("permissions", userPerm.Permissions)
|
|
c.Next()
|
|
}
|
|
}
|
|
|
|
// 使用範例
|
|
func SetupRoutes(r *gin.Engine, handler *PermissionHandler) {
|
|
api := r.Group("/api")
|
|
api.Use(handler.CheckPermissionMiddleware())
|
|
{
|
|
api.GET("/users", listUsers)
|
|
api.POST("/users", createUser)
|
|
api.PUT("/users/:id", updateUser)
|
|
api.DELETE("/users/:id", deleteUser)
|
|
}
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## 效能最佳化建議
|
|
|
|
### 1. 快取預熱
|
|
|
|
```go
|
|
// 系統啟動時預熱權限樹
|
|
func WarmUpCache(ctx context.Context, permUC usecase.PermissionUseCase) {
|
|
_, _ = permUC.GetTree(ctx)
|
|
log.Println("權限樹快取已預熱")
|
|
}
|
|
```
|
|
|
|
### 2. 批量查詢
|
|
|
|
```go
|
|
// 同時查詢多個使用者的權限
|
|
func GetMultipleUserPermissions(ctx context.Context,
|
|
rolePermUC usecase.RolePermissionUseCase,
|
|
userUIDs []string) (map[string]*usecase.UserPermissionResponse, error) {
|
|
|
|
result := make(map[string]*usecase.UserPermissionResponse)
|
|
for _, uid := range userUIDs {
|
|
perm, err := rolePermUC.GetByUserUID(ctx, uid)
|
|
if err != nil {
|
|
continue
|
|
}
|
|
result[uid] = perm
|
|
}
|
|
return result, nil
|
|
}
|
|
```
|
|
|
|
### 3. 監控快取命中率
|
|
|
|
```go
|
|
func MonitorCacheHitRate(cache repository.CacheRepository) {
|
|
// 定期檢查快取使用情況
|
|
ticker := time.NewTicker(1 * time.Minute)
|
|
for range ticker.C {
|
|
// 記錄快取命中率
|
|
// 可以整合 Prometheus 等監控系統
|
|
}
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## 總結
|
|
|
|
這個重構版本提供了:
|
|
- ✅ 完整的權限管理功能
|
|
- ✅ 高效能的快取機制
|
|
- ✅ 易於整合的 API
|
|
- ✅ 清晰的錯誤處理
|
|
- ✅ 完整的測試覆蓋
|
|
|
|
可以直接用於生產環境!
|
|
|