从壹开始【NetCore3.0】 46 ║ 受权认证:自定义返回格式

前言

哈喽你们好,立刻就要年底了,距离新的一年,只有50天了,春节是75天。前端

在这个时节内,天气逐渐变凉,可是你们的心确定很热吧,由于发生了两件大事:git

一、双十一买买买,在这个让人激动又纠结的一天,你们有没有被像 “高考命题组” 般的优惠方案搞得云里来雾里去?最终,我选择了 <不买东西优惠100%> 的最优解方案,实际上是 Q I O N G 第二声😂。github

二、还有一个特别轰动的,当属前两天上海举办的 "中国.NET开发者峰会,能够这样读NET Conf(c,o,n ,f)",真的特别轰动,各路大神齐聚,只惋惜我当时正在开启微讲堂,为了履行我上次公众号点赞的诺言——10小时入门net core的远程视频授课🤣。web

 

言归正传,曾几什么时候,在某微信群讨论 Http 状态码的时候,被某大佬给怼了一下,具体的内容就不说了,反正如今的返回状态码无非就那两个方案,一个是用 RESTFul 风格,彻底经过 http状态码来处理,另外一个就是经过 自定义返回内容,好比json的格式,把状态信息放到返回内容里边,最终我没有遵从他的意见,仍是坚持我本身的风格(状态码+自定义格式),具体的内容我都会在下面详细的说明的,恰逢QQ群里有一个小伙伴也说到了关于封装状态码的问题,其实我已经写了,只不过他的更优雅,更漂亮,因此我就用他的方案了:json

 

投稿人:QQ群:菜工飞非→飛api

 

主题:封装受权认证的自定义返回格式。微信

代码:Blog.Core 主分支websocket

具体内容:详见下文。app

 

 

 

 

1、两种返回格式的思考

在上边的文章中呢,我和某大佬基于返回格式简单的代表了下我的的立场,其实我本身也懂,无非就那么两个状况:异步

 

一、彻底基于 HTTP 返回格式状态码

说这个可能有点儿抽象,我举个例子你们就懂了:

namespace Microsoft.AspNetCore.Http
{
    public static class StatusCodes
    {
        public const int Status100Continue = 100;
        public const int Status101SwitchingProtocols = 101;
        public const int Status102Processing = 102;
        public const int Status200OK = 200;

        // 等等等等

        public const int Status400BadRequest = 400;
        public const int Status401Unauthorized = 401;
        public const int Status402PaymentRequired = 402;
        public const int Status403Forbidden = 403;
        public const int Status404NotFound = 404;
        public const int Status405MethodNotAllowed = 405;
        public const int Status406NotAcceptable = 406;

        public const int Status414RequestUriTooLong = 414;
        public const int Status414UriTooLong = 414;
        public const int Status415UnsupportedMediaType = 415;
        public const int Status416RangeNotSatisfiable = 416;
        public const int Status416RequestedRangeNotSatisfiable = 416;
        public const int Status417ExpectationFailed = 417;
        public const int Status418ImATeapot = 418;
        public const int Status419AuthenticationTimeout = 419;
        public const int Status421MisdirectedRequest = 421;
        public const int Status422UnprocessableEntity = 422;
        public const int Status423Locked = 423;
        public const int Status424FailedDependency = 424;
     
        // 等等等等

        public const int Status500InternalServerError = 500;
        public const int Status501NotImplemented = 501;
        public const int Status502BadGateway = 502;
        public const int Status503ServiceUnavailable = 503;
        public const int Status504GatewayTimeout = 504;
        public const int Status505HttpVersionNotsupported = 505;
        public const int Status506VariantAlsoNegotiates = 506;
        public const int Status507InsufficientStorage = 507;
        public const int Status508LoopDetected = 508;
        public const int Status510NotExtended = 510;
        public const int Status511NetworkAuthenticationRequired = 511;
    }
}

 

 

 上边的就是官方给定的 Http 状态码,我删了一些,你们能够看出来,官方给的特别多,也特别的全,已经能知足咱们平时开发的全部须要,彻底没问题,并且呢,这样还有一个好处,就是好比前端的项目,好比 VUE ,能够根据 http 状态码来进行拦截器进行封装,而不用看返回结果了,单单从 statuscode 上,就直接统一拦截,这样看似特别完美,那为啥还会有第二种解决方案呢,请继续往下看。

 

二、自定义返回格式内容

上边的方法真的就特别完美么,首先,拦截器这个优势,并非只能用在拦截 http statuscode 上,针对具体的返回内容也能够拦截。

其次,你们可能偶尔会遇到过这个状况,就是访问微信或者什么的时候,会出现提示 “5003 xxxxxx异常”,你们能够看一下,这个返回状态码,http 是没有的。

并且,websocket 也并无那些所谓的 404 、503吧,这个时候就须要咱们去自定义,好比这样的:

 

 

 这就是第二种解决方案,这两种方案其实一直都存在咱们的平时开发过程当中的,固然我是都在用的,我目前本身的开源项目里,用的是第一种解决方案,偶尔也会有第二种,公司的某些项目里,用的是第二种,由于有时候状态信息太多,必须去自定义,因此这两种方案我都是支持的,也不用说这个不对,那个错误,并且我也同时用了这两个

 

那既然两种都支持,若是两个我都想用,怎么封装一下呢,没问题,我就在 Blog.Core 项目里,对 受权认证 返回格式封装一下,你们看看吧,原理我之后会在直播里讲,这里就不细说了,直接讲操做步骤。

 

 

2、自定义受权认证返回格式

一、复杂的策略受权

那既然说到了返回格式,确定得有一个场景,那我就用个人复杂策略受权 PermissionHandler.cs 来举例子,你们平时也都用过,我在本周三的直播中,会详细说名这个复杂策略受权的运行机制,到时候也会有录屏,你们到时候看看就知道了,这里不细说。

简单来讲,就是获取当前 token 的角色信息和访问的URL地址,作匹配和判断,判断是否有权限,有,就 succeed,没有就 failed(这里多是 401 ,也多是403)。

当没有登陆的时候,就是 没有登陆,或者token过时的时候,咱们就 failed,会自动返回 401;

当token还有效,可是不匹配Role 和 URL 的时候,咱们返回 failed,会自动返回 403 状态码;

 

这里截图部分代码,注意下,这里若是你以前写其余返回内容了,要删掉,只保留 failed 和 return

 

 

 

 

可是,虽然是返回 401 和 403了,他们是这样的,这种很差看,并且也没有具体的响应 Message,不太友好

 

 

 

因此咱们就须要自定义返回内容的格式。

 

二、定义响应实体类

我这里写了一个很 low 的类,具体就是那个意思,你们看看便可,有更优雅的能够帮忙说说,或者提交个 PR:

namespace Blog.Core.AuthHelper.Policys
{
    public class ApiResponse
    {
        public int Status { get; set; } = 404;
        public object Value { get; set; } = "No Found";

        public ApiResponse(StatusCode apiCode)
        {
            switch (apiCode)
            {
                case StatusCode.CODE401:
                    {
                        Status = 401;
                        Value = "很抱歉,您无权访问该接口,请确保已经登陆!";
                    }
                    break;
                case StatusCode.CODE403:
                    {
                        Status = 403;
                        Value = "很抱歉,您的访问权限等级不够,联系管理员!";
                    }
                    break;
            }
        }
    }

    public enum StatusCode
    {
        CODE401,
        CODE403,
        CODE404,
        CODE500
    }

}

 

这个实体类,是用来返回响应内容的,如何使用,请往下看。

 

三、定义响应处理器

那咱们既然自定义了响应内容,就须要定义响应处理器,方法就是继承抽象类 AuthenticationHandler<TOptions> ,而后重写方法:

namespace Blog.Core.AuthHelper
{
    public class ApiResponseHandler : AuthenticationHandler<AuthenticationSchemeOptions>
    {
        public ApiResponseHandler(IOptionsMonitor<AuthenticationSchemeOptions> options, ILoggerFactory logger, UrlEncoder encoder, ISystemClock clock) : base(options, logger, encoder, clock)
        {
        }

        protected override Task<AuthenticateResult> HandleAuthenticateAsync()
        {
            throw new NotImplementedException();
        }
        protected override async Task HandleChallengeAsync(AuthenticationProperties properties)
        {
            Response.ContentType = "application/json";
            Response.StatusCode = StatusCodes.Status401Unauthorized;
            await Response.WriteAsync(JsonConvert.SerializeObject(new ApiResponse(StatusCode.CODE401)));
        }

        protected override async Task HandleForbiddenAsync(AuthenticationProperties properties)
        {
            Response.ContentType = "application/json";
            Response.StatusCode = StatusCodes.Status403Forbidden;
            await Response.WriteAsync(JsonConvert.SerializeObject(new ApiResponse(StatusCode.CODE403)));
        }

    }
}

 

这里很简单,只是重写了两个异步方法,而后将内容 Response 出去便可。

 

 

四、替换默认Scheme方案

在上边咱们说到了,咱们的认证服务 services.AddAuthentication() 它本身有一套返回格式和内容,就是上边截图的内容,那咱们要修改,就须要给替换掉:

 

 

 // 开启Bearer认证
 services.AddAuthentication(o=> {
     o.DefaultScheme = JwtBearerDefaults.AuthenticationScheme;
     o.DefaultChallengeScheme = nameof(ApiResponseHandler);
     o.DefaultForbidScheme = nameof(ApiResponseHandler);
 })
  // 添加JwtBearer服务
  .AddJwtBearer(o =>
  {
      o.TokenValidationParameters = tokenValidationParameters;
      o.Events = new JwtBearerEvents
      {
          OnAuthenticationFailed = context =>
          {
              // 若是过时,则把<是否过时>添加到,返回头信息中
              if (context.Exception.GetType() == typeof(SecurityTokenExpiredException))
              {
                  context.Response.Headers.Add("Token-Expired", "true");
              }
              return Task.CompletedTask;
          }
      };
  })
  .AddScheme<AuthenticationSchemeOptions, ApiResponseHandler>(nameof(ApiResponseHandler), o => { });

 

这个你们要注意一下,我已经把 Starup 中的服务都提取出来了,一共十个,这样你们在学习的时候,更方便。

到目前为止,咱们就已经修改完成了,咱们能够看看效果:

 

 

不只使用了 HTTP 的 StatusCode 状态码,同时也能够自定义返回内容,两个方案都兼容了,具体本身项目如何去使用,就看本身的需求了。

 

 

3、预告

这两天从新开始写 IdentityServer4 了,打算将咱们的项目统一整合到 Ids 的受权服务中内心,同时也会录一个视频教程,由于 Blog.Core 的视频教程已经完结,下一个是 Blog.IdentityServer 的视频教程。

若是你有什么问题,或者疑问,或者想了解的,请留言评论。

 

4、Github && Gitee

https://github.com/anjoy8/Blog.Core

https://gitee.com/laozhangIsPhi/Blog.Core

相关文章
相关标签/搜索