天天看點

【.NET Core 3.0】 46 ║ 授權認證:自定義傳回格式前言一、兩種傳回格式的思考二、自定義授權認證傳回格式三、預告四、Github && Gitee

前言

哈喽大家好,馬上就要年末了,距離新的一年,隻有50天了,春節是75天。

【.NET Core 3.0】 46 ║ 授權認證:自定義傳回格式前言一、兩種傳回格式的思考二、自定義授權認證傳回格式三、預告四、Github && Gitee

在這個時節内,天氣逐漸變涼,但是大家的心肯定很熱吧,因為發生了兩件大事:

1、雙十一買買買,在這個讓人激動又糾結的一天,大家有沒有被像 “聯考命題組” 般的優惠方案搞得雲裡來霧裡去?最終,我選擇了 <不買東西優惠100%> 的最優解方案,其實是 Q I O N G 第二聲😂。

2、還有一個特别轟動的,當屬前兩天上海舉辦的 "中國.NET開發者峰會,可以這樣讀NET Conf(c,o,n ,f)",真的特别轟動,各路大神齊聚,隻可惜我當時正在開啟微講堂,為了履行我上次公衆号點贊的諾言——10小時入門net core的遠端視訊授課🤣。

言歸正傳,曾幾何時,在某微信群讨論 Http 狀态碼的時候,被某大佬給怼了一下,具體的内容就不說了,反正現在的傳回狀态碼無非就那兩個方案,一個是用 RESTFul 風格,完全通過 http狀态碼來處理,另一個就是通過 自定義傳回内容,比如json的格式,把狀态資訊放到傳回内容裡邊,最終我沒有聽從他的意見,還是堅持我自己的風格(狀态碼+自定義格式),具體的内容我都會在下面詳細的說明的,恰逢QQ群裡有一個小夥伴也說到了關于封裝狀态碼的問題,其實我已經寫了,隻不過他的更優雅,更漂亮,是以我就用他的方案了:

投稿人:QQ群:菜工 、 飛非→飛

主題:封裝授權認證的自定義傳回格式。

代碼:Blog.Core 主分支

具體内容:詳見下文。

【.NET Core 3.0】 46 ║ 授權認證:自定義傳回格式前言一、兩種傳回格式的思考二、自定義授權認證傳回格式三、預告四、Github &amp;&amp; Gitee

一、兩種傳回格式的思考

在上邊的文章中呢,我和某大佬基于傳回格式簡單的表明了下個人的立場,其實我自己也懂,無非就那麼兩個情況:

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;
    }
}           

複制

【.NET Core 3.0】 46 ║ 授權認證:自定義傳回格式前言一、兩種傳回格式的思考二、自定義授權認證傳回格式三、預告四、Github &amp;&amp; Gitee

上邊的就是官方給定的 Http 狀态碼,我删了一些,大家可以看出來,官方給的特别多,也特别的全,已經能滿足我們平時開發的所有需要,完全沒問題,而且呢,這樣還有一個好處,就是比如前端的項目,比如 VUE ,可以根據 http 狀态碼來進行攔截器進行封裝,而不用看傳回結果了,單單從 statuscode 上,就直接統一攔截,這樣看似特别完美,那為啥還會有第二種解決方案呢,請繼續往下看。

2、自定義傳回格式内容

上邊的方法真的就特别完美麼,首先,攔截器這個優點,并不是隻能用在攔截 http statuscode 上,針對具體的傳回内容也可以攔截。

其次,大家可能偶爾會遇到過這個情況,就是通路微信或者什麼的時候,會出現提示 “5003 xxxxxx異常”,大家可以看一下,這個傳回狀态碼,http 是沒有的。

而且,websocket 也并沒有那些所謂的 404 、503吧,這個時候就需要我們去自定義,比如這樣的:

【.NET Core 3.0】 46 ║ 授權認證:自定義傳回格式前言一、兩種傳回格式的思考二、自定義授權認證傳回格式三、預告四、Github &amp;&amp; Gitee

這就是第二種解決方案,這兩種方案其實一直都存在我們的平時開發過程中的,當然我是都在用的,我目前自己的開源項目裡,用的是第一種解決方案,偶爾也會有第二種,公司的某些項目裡,用的是第二種,因為有時候狀态資訊太多,必須去自定義,是以這兩種方案我都是支援的,也不用說這個不對,那個錯誤,而且我也同時用了這兩個。

那既然兩種都支援,如果兩個我都想用,怎麼封裝一下呢,沒問題,我就在 Blog.Core 項目裡,對 授權認證 傳回格式封裝一下,大家看看吧,原理我以後會在直播裡講,這裡就不細說了,直接講操作步驟。

二、自定義授權認證傳回格式

1、複雜的政策授權

那既然說到了傳回格式,肯定得有一個場景,那我就用我的複雜政策授權 PermissionHandler.cs 來舉例子,大家平時也都用過,我在本周三的直播中,會詳細說明這個複雜政策授權的運作機制,到時候也會有錄屏,大家到時候看看就知道了,這裡不細說。

簡單來說,就是擷取目前 token 的角色資訊和通路的URL位址,做比對和判斷,判斷是否有權限,有,就 succeed,沒有就 failed(這裡可能是 401 ,也可能是403)。

當沒有登入的時候,就是 沒有登入,或者token過期的時候,我們就 failed,會自動傳回 401;

當token還有效,但是不比對Role 和 URL 的時候,我們傳回 failed,會自動傳回 403 狀态碼;

這裡截圖部分代碼,注意下,這裡如果你之前寫其他傳回内容了,要删掉,隻保留 failed 和 return:

【.NET Core 3.0】 46 ║ 授權認證:自定義傳回格式前言一、兩種傳回格式的思考二、自定義授權認證傳回格式三、預告四、Github &amp;&amp; Gitee

但是,雖然是傳回 401 和 403了,他們是這樣的,這種不好看,而且也沒有具體的響應 Message,不太友好

【.NET Core 3.0】 46 ║ 授權認證:自定義傳回格式前言一、兩種傳回格式的思考二、自定義授權認證傳回格式三、預告四、Github &amp;&amp; Gitee

是以我們就需要自定義傳回内容的格式。

2、定義響應實體類

我這裡寫了一個很 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
    }

}           

複制

複制

這個實體類,是用來傳回響應内容的,如何使用,請往下看。

3、定義響應處理器

那我們既然自定義了響應内容,就需要定義響應處理器,方法就是繼承抽象類 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 出去即可。

4、替換預設Scheme方案

在上邊我們說到了,我們的認證服務 services.AddAuthentication() 它自己有一套傳回格式和内容,就是上邊截圖的内容,那我們要修改,就需要給替換掉:

【.NET Core 3.0】 46 ║ 授權認證:自定義傳回格式前言一、兩種傳回格式的思考二、自定義授權認證傳回格式三、預告四、Github &amp;&amp; Gitee
// 開啟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 中的服務都提取出來了,一共十個,這樣大家在學習的時候,更友善。

到目前為止,我們就已經修改完成了,我們可以看看效果:

【.NET Core 3.0】 46 ║ 授權認證:自定義傳回格式前言一、兩種傳回格式的思考二、自定義授權認證傳回格式三、預告四、Github &amp;&amp; Gitee

不僅使用了 HTTP 的 StatusCode 狀态碼,同時也可以自定義傳回内容,兩個方案都相容了,具體自己項目如何去使用,就看自己的需求了。

三、預告

這兩天重新開始寫 IdentityServer4 了,打算将我們的項目統一整合到 Ids 的授權服務中心裡,同時也會錄一個視訊教程,因為 Blog.Core 的視訊教程已經完結,下一個是 Blog.IdentityServer 的視訊教程。

如果你有什麼問題,或者疑問,或者想了解的,請留言評論。

四、Github && Gitee

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

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