我很常寫小服務協助手工作業自動化,這類簡單 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

Exmaple of using middleware to check API key in ASP.NET Core minimal API.


Comments

# by HI

黑大 ASP.NET Core 基礎 - Middleware的網址錯囉 應該是這個 https://blog.darkthread.net/blog/aspnetcore-middleware-lab/

# by Jeffrey

to HI, 謝謝,已修正。

Post a comment