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/

posted @ 2021-08-21 02:47  happyray  阅读(2502)  评论(4编辑  收藏  举报
又是今年忆去年