13-2.ASP.NET Core Web API 入門教學 - AuthorizationFilter之自訂權限驗證

ASP.NET Core Web API 入門教學

影片講解


很多時候光靠內建的認證是不夠了,還是需要一情況寫一個符合自己系統的認證機制

這個時候就可以利用AuthorizationFilter來達成

首先先建立一個類別TodoAuthorizationFilter.cs,然後繼承IAuthorizationFilter介面

public class TodoAuthorizationFilter : IAuthorizationFilter
{
    public void OnAuthorization(AuthorizationFilterContext context)
    {
    }
}

其中要實作OnAuthorization(),這裡面就是放你要自訂驗證邏輯的地方。

你可以在這裡寫Cookie驗證,不過今天我這邊示範一個簡單的取得token驗證

那token我們都會放在header裡面,欄位會取名為Authorization

所以我們就可以用以下程式碼取得token值

bool tokenFlag = context.HttpContext.Request.Headers.TryGetValue("Authorization", out StringValues outValue);

這邊是個try方法,結果會是布林,有值就會是true,然後會產生出一個字串

所以我們程式可以這樣寫

if (tokenFlag)
{
    if (outValue != "123")
    {       
    }
}
else
{
}

先判斷有沒有取到值,有的話再進行值得驗證,那接著你就會有你的驗證邏輯,不過我這邊就很簡單的寫死有沒有等於123。當沒有等於123就代表驗證失敗,我就要返回失敗訊息,另外沒取到值依樣返回失敗訊息。

那這邊我們可以自訂一個回應格式。

public class 回應格式
{
    public dynamic Data { get; set; }
    public int HttpCode { get; set; }
    public string ErrorMessage { get; set; }
}

這是我個人的習慣,除了可能會返回的data,還會有其他資訊,例如HttpCode和錯誤訊息等等。

所以整個完整的程式碼會是

public class TodoAuthorizationFilter : IAuthorizationFilter
{
    public void OnAuthorization(AuthorizationFilterContext context)
    {

    var ignore = (from a in context.ActionDescriptor.EndpointMetadata
              where a.GetType() == typeof(AllowAnonymousAttribute)
              select a).FirstOrDefault();

        if (ignore == null)
        {
            bool tokenFlag = context.HttpContext.Request.Headers.TryGetValue("Authorization", out StringValues outValue);

            if (tokenFlag)
            {
                if (outValue != "123")
                {
                    context.Result = new JsonResult(new 回應格式()
                    {
                        Data = "test1",
                        HttpCode = 401,
                        ErrorMessage = "沒有登入"
                    });
                }
            }
            else
            {
                context.Result = new JsonResult(new 回應格式()
                {
                    Data = "test2",
                    HttpCode = 401,
                    ErrorMessage = "沒有登入"
                });
            }
        }
    }
}

這邊只要context.Result被設定值後,程式到這邊就會停止並回傳結果,反之沒設定值就等於通過認證

那我個人在前端會習慣都回200,然後額外再訊息的部分再說明現在是什麼情況或代碼

會這麼做是因為我想要有個區分,區分哪些是我自己判斷的,或程式判斷的

也就是說,我自己判斷的http狀態碼都會是200,其他非200的就是程式內建判斷的

這是我個人習慣啦,如果你堅持狀態碼也要跟著變,你可以下以下指令

context.HttpContext.Response.StatusCode = 401;

那這支如果要下再全域生效的話,同樣是到Startup.cs的ConfigureServices區塊下

services.AddMvc(options =>
{
    options.Filters.Add(new TodoAuthorizationFilter());
});

那同樣會跟上一篇cookie會有一樣的問題,就是變成登入api的也會受限制,所以我們才在前面加上一段判斷。

var ignore = (from a in context.ActionDescriptor.EndpointMetadata
              where a.GetType() == typeof(AllowAnonymousAttribute)
              select a).FirstOrDefault();

這邊我們可以借用內建的AllowAnonymous標籤,只要有加上AllowAnonymous的api我們就略過權限檢查。

另一個簡化寫法

var ignore = context.ActionDescriptor.EndpointMetadata.OfType<AllowAnonymousAttribute>().Any();

那我們要如何取得注入的服務呢?很簡單,一樣是利用建構子的方式取得即可。

但如果我們要像這樣[TodoAuthorizationFilter(Role ="333")]傳值進來

就不能寫建構子,所以我們可以改由以下方式取得服務

TodoContext _todoContext = (TodoContext)context.HttpContext.RequestServices.GetService(typeof(TodoContext));

以上是大致上會用到的程式碼,如果想看更詳細的操作流程可以看影片。

新手分享學習成果,若有錯誤,煩請告知修正,感謝🙏

範例檔:下載




Copyright © 凱哥寫程式 2022 | Powered by TalllKai ❤