C#类库中类与方法的自定义权限验证属性最佳实践咨询
更优的权限验证最佳实践
你当前在服务构造函数中通过自定义属性做权限验证的方式,存在职责不清晰、灵活性不足的问题(构造函数核心职责是初始化对象,而非业务级别的权限校验),以下是几个更合理的实践方案:
1. 抽离权限验证到独立授权服务
- 定义专门的
IAuthorizationService接口,封装权限校验逻辑:public interface IAuthorizationService { void ValidateAccess(IToken token, params UserLevel[] allowedLevels); // 也可以返回bool+错误信息,避免直接抛异常,让调用方决定处理方式 bool TryValidateAccess(IToken token, params UserLevel[] allowedLevels, out string errorMessage); } public class AuthorizationService : IAuthorizationService { public void ValidateAccess(IToken token, params UserLevel[] allowedLevels) { if (token == null || !allowedLevels.Contains(token.UserLevel)) { throw new UnauthorizedAccessException("当前用户级别无权访问该服务"); } } } - 各个服务类依赖
IAuthorizationService,在业务方法执行前调用验证逻辑,而非构造函数:
这种方式把权限校验和服务初始化分离,职责更清晰,也支持不同方法配置不同权限的场景。public class JokerService { private readonly IAuthorizationService _authService; private readonly IToken _token; public JokerService(IAuthorizationService authService, IToken token) { _authService = authService; _token = token; } public void DoJokerStuff() { // 方法执行前校验权限 _authService.ValidateAccess(_token, UserLevel.UserGoer, UserLevel.UserMaker); // 业务逻辑... } }
2. 结合属性标记与装饰器模式实现AOP校验
- 自定义权限标记属性:
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)] public class AuthorizeAttribute : Attribute { public UserLevel[] AllowedLevels { get; } public AuthorizeAttribute(params UserLevel[] allowedLevels) { AllowedLevels = allowedLevels; } } - 给服务类创建装饰器,统一拦截方法调用并校验权限(可以用Castle DynamicProxy等第三方库实现动态代理,或者手动写装饰器):
这种方式可以避免在每个方法里重复写校验代码,通过AOP统一处理权限,扩展性更强。public class JokerServiceDecorator : IJokerService { private readonly IJokerService _innerService; private readonly IAuthorizationService _authService; private readonly IToken _token; public JokerServiceDecorator(IJokerService innerService, IAuthorizationService authService, IToken token) { _innerService = innerService; _authService = authService; _token = token; } [Authorize(UserLevel.UserGoer, UserLevel.UserMaker)] public void DoJokerStuff() { // 读取方法上的权限属性并校验 var attribute = typeof(IJokerService).GetMethod(nameof(DoJokerStuff)) .GetCustomAttribute<AuthorizeAttribute>(); if (attribute != null) { _authService.ValidateAccess(_token, attribute.AllowedLevels); } _innerService.DoJokerStuff(); } }
3. 服务-权限映射+工厂模式
- 在类库初始化时,注册服务类型与允许级别映射:
public static class ServiceAuthorizationConfig { public static Dictionary<Type, UserLevel[]> ServiceToAllowedLevels { get; } = new() { { typeof(JokerService), new[] { UserLevel.UserGoer, UserLevel.UserMaker } }, // 其他服务映射... }; } - 实现服务工厂,在创建服务实例前先校验权限:
这种方式把权限校验前置到服务创建环节,确保只有权限符合的用户能拿到服务实例,避免后续调用时才报错。public class ServiceFactory { private readonly IAuthorizationService _authService; public ServiceFactory(IAuthorizationService authService) { _authService = authService; } public T CreateService<T>(IToken token) where T : class { // 校验当前服务的权限 if (ServiceAuthorizationConfig.ServiceToAllowedLevels.TryGetValue(typeof(T), out var allowedLevels)) { _authService.ValidateAccess(token, allowedLevels); } // 创建服务实例(可结合依赖注入容器) return Activator.CreateInstance(typeof(T), token) as T; } }
关于构造函数校验的不足
构造函数中做权限校验的问题在于:
- 违反单一职责:构造函数核心是初始化对象,混入业务校验逻辑会让类的职责变杂,不利于测试和维护。
- 灵活性差:如果服务中部分方法需要不同权限,构造函数校验无法区分,只能统一限制整个服务。
- 调试难度高:构造函数抛出异常会导致对象创建失败,难以定位是初始化问题还是权限问题。
内容的提问来源于stack exchange,提问作者Fatih
相关产品推荐
相关产品推荐

