假設一種情況:項目中需要做認證和權限控制,而且需要權限才能訪問的控制器要遠多於可以匿名訪問的(類似AO系統那樣,登陸了才能用)。
那在每個控制器上加一個 [Authorize] 是能解決問題,反正正我是覺得麻煩。
而且Core自帶的權限認證機制不滿足於復雜的身份權限認證,打算像在Framework中一樣注冊一個全局過濾器,然后用 [AllowAnonymous] 來放行可以匿名的控制器或者方法。
按照官方文檔,自定義身份過濾器推薦實現 IAuthorizationFilter 或者 IAsyncAuthorizationFilter 接口,再順便給他們定義為中間件更好。
例如
1 public class MyAuthorizeFilter : IAuthorizationFilter 2 { 3 public void OnAuthorization(AuthorizationFilterContext context) 4 { 5 //do something... 6 } 7 } 8 9 public class MyAsyncAuthorizeFilter : IAsyncAuthorizationFilter 10 { 11 public Task OnAuthorizationAsync(AuthorizationFilterContext context) 12 { 13 //do someting...14 } 15 }
MyAuthorizeFilter
然后在Startup.cs中注冊全局過濾器
1 public void ConfigureServices(IServiceCollection services) 2 { 3 services.AddMvc(option => { option.Filters.Add(typeof(MyAuthorizeFilter)); }); 4 }
可是運行時發現,自定義的過濾器無法阻止那些沒有授權的請求!這是為什么?
在控制器上加上 [Authorize] 調試

發現實際上我的過濾器已經被加到了過濾器列隊里,但是本身並沒有執行任何動作。
其實這里也是我自己犯傻了,人家就是一個接口而已,肯定沒有任何操作,單純繼承接口以后指望人家能做什么呢。
那我們既要實現原生的權限認證機制(畢竟像未登錄跳轉等功能不用自己實現了),還要增加自定義的認證機制。
后來發現 [Authorize] 屬性會被注冊為AuthorizeFliter過濾器。那就妥了,繼承然后重寫其實現就好。
我的過濾器就變成了這樣。
1 public class MyAuthorizeFilter : AuthorizeFilter 2 { 3 4 private static AuthorizationPolicy _policy_ = new AuthorizationPolicy(new[] { new DenyAnonymousAuthorizationRequirement() }, new string[] { }); 5 6 public MyAuthorizeFilter() : base(_policy_) { } 7 8 public override async Task OnAuthorizationAsync(AuthorizationFilterContext context) 9 { 10 await base.OnAuthorizationAsync(context); 11 Console.WriteLine("權限檢測"); 12 } 13 }
說明一下實現AuthorizeFilter基類,必須有一個過濾策略,也就是,這里我采用的是最基礎的DenyAnonymousAuthorizationRequirement(阻止匿名身份的請求)
運行,過濾器可以正常過濾沒有授權的請求了,但是無論授權與否,或者是否可匿名訪問,均會執行“權限檢測”那里。
這是為啥?
繼續調試。

發現 [AllowAnonymous] 也被注冊成了過濾器。
修改代碼,最終成了這樣
1 public class MyAuthorizeFilter : AuthorizeFilter 2 { 3 4 private static AuthorizationPolicy _policy_ = new AuthorizationPolicy(new[] { new DenyAnonymousAuthorizationRequirement() }, new string[] { }); 5 6 public MyAuthorizeFilter() : base(_policy_) { } 7 8 public override async Task OnAuthorizationAsync(AuthorizationFilterContext context) 9 { 10 await base.OnAuthorizationAsync(context); 11 if (!context.HttpContext.User.Identity.IsAuthenticated || 12 context.Filters.Any(item => item is IAllowAnonymousFilter)) return; 13 //do something 14 } 15 }
僅作為一個簡單的學習筆記。如果有更好的方法歡迎指教。
又及:
在某篇博客中見過一個問題,大概就是說 context.HttpContext.User.Identity.IsAuthenticate 的值恆定false,后來采用了一大堆不啦不啦的方法自己實現了獲取認證狀態的方法。這篇博客我找不到了。
我也遇到了類似的問題,后來發現是在startup的Configure中沒有啟用身份認證 app.UseAuthentication() ,根據自己的方法自行選擇,例如我后來用IdentityServer4,就變成了 app.UseIdentityServer();
