1. 程式人生 > WINDOWS開發 >ASP.NET Core 中基於 API Key 對私有 Web API 進行保護

ASP.NET Core 中基於 API Key 對私有 Web API 進行保護

原文:ASP.NET Core 中基於 API Key 對私有 Web API 進行保護

這兩天遇到一個應用場景,需要對內網呼叫的部分 web api 進行安全保護,只允許請求頭賬戶包含指定 key 的客戶端進行呼叫。在網上找到一篇英文博文 ASP.NET Core - Protect your API with API Keys,該文中的程式碼完美基於 ASP.NET Core 內建的鑑權(Authentication) 與授權(Authorization)機制解決了這個問題,於是站在巨人的肩上自己實現了一遍,在這篇隨筆中做個記錄。

ASP.NET Core Authentication 與 Authorization 實現的開原始碼在

https://github.com/aspnet/AspNetCore/tree/master/src/Security

使用 API Key 對私有 Web API 進行保護,實際就是通過自定義的 AuthenticationHandler 對 ASP.NET Core Authentication 的鑑權能力進行擴充套件,具體實現分4步。

1)實現配角(配置選項)ApiKeyAuthenticationOptions

public class ApiKeyAuthenticationOptions : AuthenticationSchemeOptions
{
    public const
string DefaultScheme = "ApiKey"; public string Scheme { get; set; } = DefaultScheme; public string AuthenticationType { get; set; } = DefaultScheme; }

這裡的示例程式很簡單,選項只用於定義 DefaultScheme 。

2)實現主角 ApiKeyAuthenticationHandler

public class ApiKeyAuthenticationHandler : AuthenticationHandler
<ApiKeyAuthenticationOptions> { private const string HEADER_NAME = "X-Api-Key"; private readonly (string Owner,string Key)[] _apiKeys = new[] { ("test","xxx123yyy456zzz") }; private readonly ApiKeyAuthenticationOptions _options; public ApiKeyAuthenticationHandler( IOptionsMonitor<ApiKeyAuthenticationOptions> options,ILoggerFactory logger,UrlEncoder Encoder,ISystemClock clock) : base(options,logger,Encoder,clock) { _options = options.CurrentValue; } protected override async Task<AuthenticateResult> HandleAuthenticateAsync() { var providedApiKey = Context.Request.Headers[HEADER_NAME].FirstOrDefault(); if (string.IsNullOrWhiteSpace(providedApiKey)) { return AuthenticateResult.NoResult(); } var apiKey = _apiKeys.FirstOrDefault(k => k.Key == providedApiKey); if (apiKey != default) { var claims = new[] { new Claim(ClaimTypes.Name,apiKey.Owner) }; var identity = new ClaimsIdentity(claims,authenticationType: _options.AuthenticationType); var identities = new List<ClaimsIdentity> { identity }; var principal = new ClaimsPrincipal(identities); var ticket = new AuthenticationTicket(principal,authenticationScheme: _options.Scheme); await Task.CompletedTask; return AuthenticateResult.Success(ticket); } return AuthenticateResult.Fail("Invalid API Key provided."); } }

在 override 方法 HandleAuthenticateAsync 中實現基於 API Key 對私有 Web API 進行保護的鑑權邏輯,根據客戶端請求頭進行驗證,如果是合法請求,就發一個 ticket 。有了這張門票, 如果只要有門票就能通過(比如加了[Authorize]宣告), 沒有其他授權要求,Authorization 就會直接放行。

3)實現跑龍套的 AuthenticationBuilderExtensions 擴充套件方法

public static class AuthenticationBuilderExtensions
{
    public static AuthenticationBuilder AddApiKeySupport(this AuthenticationBuilder authenticationBuilder)
    {
        return authenticationBuilder.AddScheme<ApiKeyAuthenticationOptions,ApiKeyAuthenticationHandler>(
            ApiKeyAuthenticationOptions.DefaultScheme,options => { });
    }

    public static AuthenticationBuilder AddApiKeySupport(this AuthenticationBuilder authenticationBuilder,Action<ApiKeyAuthenticationOptions> options)
    {
        return authenticationBuilder.AddScheme<ApiKeyAuthenticationOptions,options);
    }
}

這個擴充套件方法只是為了方便在 Startup 中新增 ApiKeyAuthenticationHandler 。

4)開始演戲
Startup.ConfigureServices 中配置 Authentication

services.AddAuthentication(options =>
{
    options.DefaultScheme = ApiKeyAuthenticationOptions.DefaultScheme;
    options.DefaultChallengeScheme = ApiKeyAuthenticationOptions.DefaultScheme;
}).AddApiKeySupport();

Startup.Configure 中新增 Authentication 與 Authorization 中介軟體(注:一定要放在 app.UseRouting 之後)

app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
    endpoints.MapControllers();
});

對需要保護的 web api 新增 [Authorize] 宣告

[Authorize]
public async Task<bool> ProtectedAction()
{
    //...
}

搞定。