ABP VNext添加全局認證(如何繼承AuthorizeFilter)

前言

目前公司採用的開發框架是ABP VNext微服務框架

最近突然發現一個問題,ABP中如果控制器或服務層沒有加 Authorize特性的話,則不會走身份認證,且不會認證Token

如圖:

 

但是項目已開發大半,一個個去補Authorize特性,工作量比較大,也容易產生遺漏

就想着以前做單體應用的時候,有個全局添加特性的方法,也就是如下代碼:

Services.AddMvc(setupAction =>
{
   setupAction.Filters.Add<AuthorizeFilter>();
});

本以爲這樣就萬事大吉了,沒想到還有坑在裏面..

我們都知道,ABP提供了服務間的動態API通訊功能,它的原理是先獲取對應服務的描述,然後通過描述來訪問對應的服務節點,

也就是 api/abp/api-definition 這個描述JSON

我們用以上的代碼添加了全局授權之後會發現api-definition也被權限管控了,由於api-definition是由ABP框架自動生成的,我們也無法在這個終結點上添加類似  AllowAnonymous 的過濾特性

 

正文

 

那麼應該如何解決這個問題呢?

首先想到的就是實現自己的授權特性,只需要繼承 IAsyncAuthorizationFilter,即可

但是如果採用自己的AuthorizationFilter,則需要重寫整個 OnAuthorizationAsync 事件.

ABP提供了角色之類的授權信息就都需要自行重寫.

後來想到,可以繼承AuthorizeFilter ,添加我們想要的過濾之後直接執行父類的方法,說幹就幹,我們繼承AuthorizeFilter ,代碼實現如下:

    public class AbpAuthorizeFilter : AuthorizeFilter
    {

        public AbpAuthorizeFilter()
            : base()
        {
        }

        public override Task OnAuthorizationAsync(AuthorizationFilterContext context)
        {
            //過濾動態API
            if (context.HttpContext.Request.Path.Value.EndsWith("/api-definition"))
            {
                return Task.CompletedTask;
            }
            return base.OnAuthorizationAsync(context);
        }

    }

 

可是當我們信心滿滿的把這個攔截器注入之後,會發現整個授權管道,壓根就不走自己的這個重寫方法.

找了很多資料,最終在官方的issues中找到了類似的疑問,Overrided OnAuthorizationAsync function from AuthorizeFilter can't work in customer class. · Issue #30025 · dotnet/aspnetcore (github.com)

是因爲在.NET 5.0 之後,AuthorizeFilter繼承了 IFilterFactory,所以在生成實例的時候其實是來自於IFilterFactory的CreateInstance方法, 我們沒有重寫這個方法,所以一直產生的還是AuthorizeFilter 實例

我們修改代碼如下:

    public class AbpAuthorizeFilter2 : AuthorizeFilter
    {

        public AbpAuthorizeFilter2()
            : base()
        {
        }

        public override Task OnAuthorizationAsync(AuthorizationFilterContext context)
        {
            //過濾動態API
            if (context.HttpContext.Request.Path.Value.EndsWith("/api-definition"))
            {
                return Task.CompletedTask;
            }
            return base.OnAuthorizationAsync(context);
        }

        IFilterMetadata CreateInstance(IServiceProvider serviceProvider)
        {
            return this;
        }
    }

  

運行後發現,在執行到這個攔截器的時候,就會報錯,提示PolicyProvider 不能爲空.  這就很納悶了,最終選擇去查看一下AuthorizeFilter的源碼,aspnetcore/src/Mvc/Mvc.Core/src/Authorization/AuthorizeFilter.cs at 1bda10b33b6cc6f3bbaceabbadb4ddd18ca6e68e · dotnet/aspnetcore (github.com)

我們發現他這個PolicyProvider對象來自於IOC容器,且在CreateInstance方法中判斷了這個類是否爲空,如果爲空則返回基類自己,代碼如下:

IFilterMetadata IFilterFactory.CreateInstance(IServiceProvider serviceProvider)
{
        if (Policy != null || PolicyProvider != null)
        {
            // The filter is fully constructed. Use the current instance to authorize.
            return this;
        }

        Debug.Assert(AuthorizeData != null);
        var policyProvider = serviceProvider.GetRequiredService<IAuthorizationPolicyProvider>();
        return AuthorizationApplicationModelProvider.GetFilter(policyProvider, AuthorizeData);
 }

 

那我們就好辦了,直接從IOC容器中拿到IAuthorizationPolicyProvider這個實現類,提供給基類即可,我們修改代碼如下:

    public class AbpAuthorizeFilter:AuthorizeFilter 
    {

        public AbpAuthorizeFilter(IServiceProvider serviceProvider)
            : base(policyProvider: serviceProvider.GetRequiredService<IAuthorizationPolicyProvider>(), authorizeData: new[] { new AuthorizeAttribute() })
        {
        }
        public void OnAuthorization(AuthorizationFilterContext context)
        {
            OnAuthorizationAsync(context);
        }

        public override Task OnAuthorizationAsync(AuthorizationFilterContext context)
        {
            //過濾動態API
            if (context.HttpContext.Request.Path.Value.EndsWith("/api-definition"))
            {
                return Task.CompletedTask;
            }
            return base.OnAuthorizationAsync(context);
        }

    }

然後修改HostModule中全局授權的方法如下(.NETCORE 是Startup)

context.Services.AddMvc(setupAction =>
{
  //添加自定義的全局攔截器
  setupAction.Filters.Add<AbpAuthorizeFilter>();

});

 

至此,我們就完成了過濾abp的描述控制器的工作.

後記

碰到奇葩問題,多看看官方源碼還是有好處的,有些實現並不是想當然的東西,還是需要實踐

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章