11

ASP.NET Core 練習 - 用 Middleware 為 Minimal API 加上 API Key 檢查

 1 year ago
source link: https://blog.darkthread.net/blog/minapi-middleware-header-chk/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

用 Middleware 為 Minimal API 加上 API Key 檢查-黑暗執行緒

我很常寫小服務協助手工作業自動化,這類簡單 Web API 許多程式碼不過一百多行,連寫帶測半天就能寫完,沒啥必要切介面拆模組裝 Swagger 走 OpenAPI,網站框架也愈精簡愈好。因此 .NET Framework 時代我酷愛 NancyFX,而 ASP.NET Core 6.0 推出的 Minimal API,最短四行就寫好一個動態網頁,更是深得我心。

Side Project 有個資料服務需要識別呼叫來源,我想到最簡單的方法是在 HTTP 請求夾帶 API Key Header,執行動作前檢查正確再放行。當然,若求嚴謹可考慮 JWT Token 等正規做法(實作方法可參考保哥這篇:如何在 ASP.NET Core 6 使用 Token-based 身份認證與授權 (JWT)),居家自用小程式我偏好 Keep It Simple and Stupid,生個 GUID 當 API Key 應該就很夠了)

要檢查 Http Header,笨但可行的方法是寫個檢查 Header 函式,在執行動作前檢查,例如:

bool chkXApiHeader(HttpRequest request) =>
    !request.Headers.TryGetValue(apiKeyHeaderName, out var apiKey)
        || apiKey != apiKeyValue;
        
app.Get("/api/action1", (context) => {
    if (!chkXApiHeader(context.Request))
        return Results.Unauthorized;
    //... api logic
});
app.Get("/api/action2", (context) => {
    if (!chkXApiHeader(context.Request))
        return Results.Unauthorized;
    //... api logic
});

一堆複製貼上的程式碼肯定不 OK 啊! Minimal API 不像 ASP.NET MVC 可以掛 Authorization Filter,但不要緊,回歸 ASP.NET Core 底層運作,我們有 Middleware 可在網頁處理流程加入各式自訂邏輯,本次的小任務將溫習 ASP.NET Core 基礎 - Middleware,為 Minimal API 加入 Header 檢查:

var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();

var apiKeyHeaderName = "X-Api-Key";
var apiKeyValue = app.Configuration["ApiKey"];
app.Use((context, next) =>
{
    if (context.Request.Path.StartsWithSegments("/api"))
    {
        // 亦可限定來源 IP 提高安全性
        // var ip = context.Connection.RemoteIpAddress.ToString();
        if (!context.Request.Headers.TryGetValue(apiKeyHeaderName, out var apiKey)
        || apiKey != apiKeyValue)
        {
            context.Response.StatusCode = 401;
            return context.Response.WriteAsync("Unauthorized");
        }
    }
    return next();
});

app.MapGet("/", () => "Dummy WebAPI");
app.MapGet("/api/new-guid", () => Guid.NewGuid());
app.MapGet("/api/get-random", () => new Random().Next());

app.Run();

寫段 PowerShell 程式驗證,呼叫 / 不需附 X-Api-Key Header,呼叫 /api/new-guid 未附 X-Api-Key Header 或給錯值會得到 HTTP 401,附加正確的 X-Api-Key 才能呼叫成功!

(Invoke-WebRequest http://localhost:5174/).Content
try {
    (Invoke-WebRequest http://localhost:5174/api/new-guid).Content
}
catch {
    $_.Exception
}
try {
    Invoke-WebRequest http://localhost:5174/api/new-guid -Headers @{ 'X-Api-Key' = 'WrongKey' }
}
catch {
    $_.Exception
}
(Invoke-WebRequest http://localhost:5174/api/new-guid -Headers @{ 'X-Api-Key' = 'CorrectKey' }).Content

Fig1_638073177075147268.png


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK