12 KiB
12 KiB
使用範例
完整範例:從零到完整權限系統
1. 初始化系統
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. 建立角色和權限
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. 指派角色給使用者
// 指派「使用者管理員」角色給使用者 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. 查詢使用者權限
// 查詢 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 權限
// 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. 更新角色權限
// 升級 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. 查詢角色列表 (含使用者數量)
// 分頁查詢所有角色
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. 查詢擁有特定權限的使用者
// 查詢所有有 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. 取得權限樹
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 範例
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. 快取預熱
// 系統啟動時預熱權限樹
func WarmUpCache(ctx context.Context, permUC usecase.PermissionUseCase) {
_, _ = permUC.GetTree(ctx)
log.Println("權限樹快取已預熱")
}
2. 批量查詢
// 同時查詢多個使用者的權限
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. 監控快取命中率
func MonitorCacheHitRate(cache repository.CacheRepository) {
// 定期檢查快取使用情況
ticker := time.NewTicker(1 * time.Minute)
for range ticker.C {
// 記錄快取命中率
// 可以整合 Prometheus 等監控系統
}
}
總結
這個重構版本提供了:
- ✅ 完整的權限管理功能
- ✅ 高效能的快取機制
- ✅ 易於整合的 API
- ✅ 清晰的錯誤處理
- ✅ 完整的測試覆蓋
可以直接用於生產環境!