ASP.NET Core下自定義授權返回結果


  今天在為項目編寫API統一返回結果的代碼時,發現不能通過Filter來定義授權失敗后的響應結果,於是我翻看了一下官方文檔和aspnetcore源碼,原來需要自定義實現IAuthorizationMiddlewareResultHandler接口。

  Asp.Net Core 5自帶的驗權中間件,在驗權失敗后,是直接返回一個401。這對於前端來說不太友好,所以我的需求是改為返回200的自定義結果。

  我通過搜索引擎查閱了一下別人實現的代碼,發現都比較復雜,我這里需求比較簡單,就不做太復雜的判斷。

  環境:ASP.NET Core 5。

  一、首先定義AuthorizationMiddlewareResultHandler類,並且實現IAuthorizationMiddlewareResultHandler接口。

 1 using Microsoft.AspNetCore.Authorization;
 2 using Microsoft.AspNetCore.Authorization.Policy;
 3 using Microsoft.AspNetCore.Http;
 4 using System.Threading.Tasks;
 5 
 6 namespace DotNet.AspNetCore.WebApi
 7 {
 8     public class AuthorizationMiddlewareResultHandler : IAuthorizationMiddlewareResultHandler
 9     {
10         public async Task HandleAsync(RequestDelegate next, HttpContext context, AuthorizationPolicy policy, PolicyAuthorizationResult authorizeResult)
11         {
12             //因為管道還沒有走到Action 所以沒有ActionResult使用 我們必須自己定義Response中的內容
13             //這里授權是否成功
14             if (!authorizeResult.Succeeded)
15             {
16                 //將狀態碼定義為200
17                 context.Response.StatusCode = 200;
18                 //使用 WriteAsJsonAsync 寫入一個自定義的返回對象 自動完成Json的序列化操作
19                 //我這里用匿名類演示 實際項目中請替換成對應的返回對象 自定義狀態碼和提示信息
20                 //身份驗證是否通過
21                 if (!context.User.Identity.IsAuthenticated)
22                     await context.Response.WriteAsJsonAsync(new { Code = 401, Message = "身份驗證不通過", Result = string.Empty });
23                 else
24                     await context.Response.WriteAsJsonAsync(new { Code = 403, Message = "沒有權限", Result = string.Empty });
25                 //注意一定要return 在這里短路管道 不要走到next 否則線程會進入后續管道 到達action中
26                 return;
27             }
28             //如果授權成功 繼續執行后續的中間件 記住一定記得next 否則會管道會短路
29             await next(context);
30         }
31     }
32 }

  二、在Startup.cs中注入單例服務,將其作為IAuthorizationMiddlewareResultHandler的實現。

1         public void ConfigureServices(IServiceCollection services)
2         {
3 // IAuthorizationMiddlewareResultHandler 用來替換框架默認的授權返回結果 4 services.AddSingleton<IAuthorizationMiddlewareResultHandler, AuthorizationMiddlewareResultHandler>();
5 }

  代碼非常簡單,很容易就自定義了授權返回結果。但是,到這里就結束了嗎?不不不,好奇的我想知道為什么要這樣寫,於是我去看了一下源碼。

  在查看了app.UseAuthentication()對應的AuthorizationMiddleware中間件后,發現在Invoke的最后有這么一段代碼:

var authorizeResult = await policyEvaluator.AuthorizeAsync(policy, authenticateResult!, context, resource);
var authorizationMiddlewareResultHandler = context.RequestServices.GetRequiredService<IAuthorizationMiddlewareResultHandler>();
await authorizationMiddlewareResultHandler.HandleAsync(_next, context, policy, authorizeResult);

  context.RequestServices.GetRequiredService<IAuthorizationMiddlewareResultHandler>()這段代碼表明,AuthorizationMiddleware中間件是通過獲取服務容器中IAuthorizationMiddlewareResultHandler的實例,來處理授權返回結果。而我們上面的AddSingleton<IAuthorizationMiddlewareResultHandler, AuthorizationMiddlewareResultHandler>()則是替換掉了框架中原有的默認實現,從而達到了我們想要的返回結果。

  嘖嘖嘖,又學會了一招如何巧妙的設計中間件。

  這里我把框架原有的實現也貼出來:

 1     public class AuthorizationMiddlewareResultHandler : IAuthorizationMiddlewareResultHandler
 2     {
 3         /// <inheritdoc />
 4         public async Task HandleAsync(RequestDelegate next, HttpContext context, AuthorizationPolicy policy, PolicyAuthorizationResult authorizeResult)
 5         {
 6             if (authorizeResult.Challenged)
 7             {
 8                 if (policy.AuthenticationSchemes.Count > 0)
 9                 {
10                     foreach (var scheme in policy.AuthenticationSchemes)
11                     {
12                         await context.ChallengeAsync(scheme);
13                     }
14                 }
15                 else
16                 {
17                     await context.ChallengeAsync();
18                 }
19 
20                 return;
21             }
22             else if (authorizeResult.Forbidden)
23             {
24                 if (policy.AuthenticationSchemes.Count > 0)
25                 {
26                     foreach (var scheme in policy.AuthenticationSchemes)
27                     {
28                         await context.ForbidAsync(scheme);
29                     }
30                 }
31                 else
32                 {
33                     await context.ForbidAsync();
34                 }
35 
36                 return;
37             }
38 
39             await next(context);
40         }
41     }

  看來還是要經常回顧一下以前的知識,持之以恆,夯實基礎。

  author:https://www.cnblogs.com/abnerwong/


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM