package service import ( "context" "fmt" "time" "with_you/internal/cache" "with_you/internal/config" "with_you/internal/pkg/auth" "with_you/internal/repository" "github.com/casbin/casbin/v3" ) // CasbinService Casbin 权限服务接口 type CasbinService interface { // Enforce 检查权限 Enforce(ctx context.Context, sub, obj, act string) (bool, error) // EnforceForUser 检查用户权限 (自动获取用户角色) EnforceForUser(ctx context.Context, userID, obj, act string) (bool, error) // AddRoleForUser 为用户添加角色 AddRoleForUser(ctx context.Context, userID, role string) error // DeleteRoleForUser 移除用户角色 DeleteRoleForUser(ctx context.Context, userID, role string) error // GetRolesForUser 获取用户所有角色 GetRolesForUser(ctx context.Context, userID string) ([]string, error) // HasRoleForUser 检查用户是否拥有指定角色 HasRoleForUser(ctx context.Context, userID, role string) (bool, error) // AddPolicy 添加策略 AddPolicy(ctx context.Context, role, resource, action string) error // RemovePolicy 移除策略 RemovePolicy(ctx context.Context, role, resource, action string) error // LoadPolicy 从数据库重新加载策略 LoadPolicy(ctx context.Context) error // ClearCache 清除权限缓存 ClearCache(ctx context.Context) error // GetPermissionsForRole 获取角色的所有权限 GetPermissionsForRole(ctx context.Context, role string) ([][]string, error) // UpdatePermissionsForRole 更新角色的权限(先删除所有权限,再添加新权限) UpdatePermissionsForRole(ctx context.Context, role string, permissions [][]string) error // GetAllPermissions 获取系统中所有权限定义 GetAllPermissions(ctx context.Context) [][]string } type casbinService struct { enforcer *casbin.Enforcer cache cache.Cache cacheTTL time.Duration roleRepo repository.RoleRepository skipRoutes map[string]bool } func NewCasbinService( enforcer *casbin.Enforcer, cache cache.Cache, roleRepo repository.RoleRepository, cfg *config.CasbinConfig, ) CasbinService { skipRoutes := make(map[string]bool) for _, route := range cfg.SkipRoutes { skipRoutes[route] = true } return &casbinService{ enforcer: enforcer, cache: cache, cacheTTL: time.Duration(cfg.CacheTTL) * time.Second, roleRepo: roleRepo, skipRoutes: skipRoutes, } } func (s *casbinService) Enforce(ctx context.Context, sub, obj, act string) (bool, error) { // 检查是否跳过权限检查 if s.skipRoutes[obj] { return true, nil } // 缓存键与策略维度对齐:sub/obj/act 直接对应 p, role, resource, action。 // user_roles 表为用户-角色真源,由 EnforceForUser 解析后再调本方法; // 这里 sub 已经是角色名,避免再走 g(user, role) 分组(model 中 matcher 已不再依赖 g)。 if s.cache != nil { cacheKey := fmt.Sprintf("casbin:policy:%s:%s:%s", sub, obj, act) if cached, ok := s.cache.Get(cacheKey); ok { if allowed, ok := cached.(bool); ok { return allowed, nil } } } // 执行权限检查 allowed, err := s.enforcer.Enforce(sub, obj, act) if err != nil { return false, err } // 缓存结果 if s.cache != nil { cacheKey := fmt.Sprintf("casbin:policy:%s:%s:%s", sub, obj, act) s.cache.Set(cacheKey, allowed, s.cacheTTL) } return allowed, nil } func (s *casbinService) EnforceForUser(ctx context.Context, userID, obj, act string) (bool, error) { // 获取用户角色 roles, err := s.GetRolesForUser(ctx, userID) if err != nil { return false, err } // 如果用户没有角色,使用默认角色 if len(roles) == 0 { roles = []string{"user"} // 默认角色 } // 检查每个角色的权限 for _, role := range roles { allowed, err := s.Enforce(ctx, role, obj, act) if err != nil { return false, err } if allowed { return true, nil } } return false, nil } func (s *casbinService) AddRoleForUser(ctx context.Context, userID, role string) error { // 真源策略:user_roles 表为唯一真源,Casbin 不再承载 g, user, role 分组策略。 // 减少双写漂移与缓存失效盲区;EnforceForUser 内部先查 DB 角色再对每个角色调 Enforce(role,...)。 if err := s.roleRepo.AddRoleForUser(ctx, userID, role); err != nil { return err } // 失效该用户的角色缓存,以及与该角色相关的策略缓存。 s.clearUserRoleCache(ctx, userID) return s.clearPolicyCache(ctx) } func (s *casbinService) DeleteRoleForUser(ctx context.Context, userID, role string) error { if err := s.roleRepo.DeleteRoleForUser(ctx, userID, role); err != nil { return err } s.clearUserRoleCache(ctx, userID) return s.clearPolicyCache(ctx) } func (s *casbinService) GetRolesForUser(ctx context.Context, userID string) ([]string, error) { // 检查缓存 if s.cache != nil { cacheKey := fmt.Sprintf("casbin:user:roles:%s", userID) if cached, ok := s.cache.Get(cacheKey); ok { if roles, ok := cached.([]string); ok { return roles, nil } } } // 从数据库获取 roles, err := s.roleRepo.GetUserRoles(ctx, userID) if err != nil { return nil, err } // 缓存结果 if s.cache != nil { cacheKey := fmt.Sprintf("casbin:user:roles:%s", userID) s.cache.Set(cacheKey, roles, s.cacheTTL) } return roles, nil } func (s *casbinService) HasRoleForUser(ctx context.Context, userID, role string) (bool, error) { return s.roleRepo.HasRole(ctx, userID, role) } func (s *casbinService) AddPolicy(ctx context.Context, role, resource, action string) error { if _, err := s.enforcer.AddPolicy(role, resource, action); err != nil { return err } return s.ClearCache(ctx) } func (s *casbinService) RemovePolicy(ctx context.Context, role, resource, action string) error { if _, err := s.enforcer.RemovePolicy(role, resource, action); err != nil { return err } return s.ClearCache(ctx) } func (s *casbinService) LoadPolicy(ctx context.Context) error { return s.enforcer.LoadPolicy() } func (s *casbinService) ClearCache(ctx context.Context) error { if s.cache != nil { // 清除所有 casbin 相关缓存:策略缓存 + 用户角色缓存。 s.cache.DeleteByPrefix("casbin:") } return nil } // clearPolicyCache 失效所有策略缓存(casbin:policy:*)。 // // 策略变更或角色变更都应调用,确保下次 Enforce 重新查 Casbin。 func (s *casbinService) clearPolicyCache(ctx context.Context) error { if s.cache != nil { s.cache.DeleteByPrefix("casbin:policy:") } return nil } // clearUserRoleCache 仅失效某个用户的角色缓存(casbin:user:roles:{userID})。 // // 用于 AddRoleForUser/DeleteRoleForUser:只刷新该用户的角色,不波及策略缓存(除非同时调 clearPolicyCache)。 // 同时失效认证管道的 Principal 缓存,让 RequireAuth 下次重新加载该用户的角色。 func (s *casbinService) clearUserRoleCache(ctx context.Context, userID string) error { if s.cache != nil { s.cache.Delete(fmt.Sprintf("casbin:user:roles:%s", userID)) s.cache.Delete(auth.PrincipalCacheKey(userID)) } return nil } // clearUserCache Deprecated 兼容旧调用,内部转调 clearUserRoleCache。 func (s *casbinService) clearUserCache(ctx context.Context, userID string) error { return s.clearUserRoleCache(ctx, userID) } func (s *casbinService) GetPermissionsForRole(ctx context.Context, role string) ([][]string, error) { return s.enforcer.GetPermissionsForUser(role) } func (s *casbinService) UpdatePermissionsForRole(ctx context.Context, role string, permissions [][]string) error { // 先删除该角色的所有权限 _, err := s.enforcer.DeletePermissionsForUser(role) if err != nil { return err } // 添加新权限 for _, perm := range permissions { if len(perm) >= 2 { resource := perm[0] action := perm[1] if _, err := s.enforcer.AddPolicy(role, resource, action); err != nil { return err } } } // 清除缓存 return s.ClearCache(ctx) } func (s *casbinService) GetAllPermissions(ctx context.Context) [][]string { permissions, _ := s.enforcer.GetPolicy() return permissions }