abp 以或的方式驗證多個 AuthorizeAttribute
前言
在使用 abp 開發業務功能時,會遇到公用同一個類的情況,在給這個類配置許可權時,就要添加多個 AuthorizeAttribute,類似下面這樣:
[Authorize(DcsPermissions.DocCenter.Doc.Default)] [Authorize(DcsPermissions.WorkingPlatform.MyDraft.Default)] public class DocAppService : DcsAppServiceBase, IDocAppService { // ...... }
但是 abp 在驗證時,會以且的方式驗證這兩個 Policy,只要一個沒有許可權,則返回 403 狀態碼。如果想以或的方式(只要有一個有許可權,那麼就返回有許可權)驗證如何做呢?通過查看 abp 源碼,我們可以新增一個 IMethodInvocationAuthorizationService 介面的實現替換掉 abp 默認的實現 MethodInvocationAuthorizationService 。這個類實現的唯一目的就是通過 AuthorizeAttribute 構造出 AuthorizationPolicy 然後使用 IAbpAuthorizationService 驗證許可權。下面看看我如何實現或的方式進行驗證許可權吧。
實現
程式碼不多,就直接看下面的程式碼吧
[Dependency(ReplaceServices = true)] public class MyMethodInvocationAuthorizationService : IMethodInvocationAuthorizationService, ITransientDependency { private readonly IAbpAuthorizationService _abpAuthorizationService; public AutobioMethodInvocationAuthorizationService(IAbpAuthorizationService abpAuthorizationService) { this._abpAuthorizationService = abpAuthorizationService; } public async Task CheckAsync(MethodInvocationAuthorizationContext context) { if ( this.AllowAnonymous(context)) { return; } var policyNames = this.GetAuthorizationDataPolicyNames(context.Method); if ( policyNames.Any() ) { var isGranted = await this._abpAuthorizationService.IsGrantedAnyAsync(policyNames); if ( !isGranted ) { throw new AbpAuthorizationException(code: AbpAuthorizationErrorCodes.GivenPolicyHasNotGranted); } } } protected virtual bool AllowAnonymous(MethodInvocationAuthorizationContext context) => context.Method.GetCustomAttributes(true).OfType<IAllowAnonymous>().Any(); protected virtual string[] GetAuthorizationDataPolicyNames(MethodInfo methodInfo) { var attributes = methodInfo .GetCustomAttributes(true) .OfType<IAuthorizeData>(); if (methodInfo.IsPublic && methodInfo.DeclaringType != null) { attributes = attributes .Union( methodInfo.DeclaringType .GetCustomAttributes(true) .OfType<IAuthorizeData>() ); } return attributes.Where(_ => !string.IsNullOrWhiteSpace(_.Policy)).Select(_ => _.Policy).ToArray(); } }
這裡面主要利益於 abp 提供了 IsGrantedAnyAsync 擴展方法。
總結
打算把這個想法提個 PR 給 abp 項目。整個擴展過程下來的感覺 abp 挺靈活的。abp 默認達不到的要求,幾乎都可以通過擴展它來解決掉。