ASP.NET Core 實現(xiàn)基于 ApiKey 的認(rèn)證
ASP.NET Core 實現(xiàn)基于 ApiKey 的認(rèn)證
Intro
之前我們有介紹過實現(xiàn)基于請求頭的認(rèn)證,今天來實現(xiàn)一個基于 ApiKey 的認(rèn)證方式,使用方式參見下面的示例
Sample
注冊認(rèn)證服務(wù)
services.AddAuthentication()
????.AddApiKey(options?=>
????{
????????options.ApiKey?=?"123456";
????????options.ApiKeyName?=?"X-ApiKey";
????});
可以根據(jù)需要是否要指定為默認(rèn)的認(rèn)證 Schema
控制器示例代碼:
[HttpGet("apiKeyTest")]
[Authorize(AuthenticationSchemes?=?ApiKeyAuthenticationDefaults.AuthenticationSchema)]
public?IActionResult?ApiKeyAuthTest()
{
????return?Ok(User.Identity);
}
因為我們沒有指定為默認(rèn)的認(rèn)證 Schema,所以在這個 Action 上我們指定了希望使用的認(rèn)證方式
調(diào)用示例:
直接調(diào)用不使用 ApiKey

使用錯誤的 ApiKey

使用正確的 ApiKey

從上面的結(jié)果可以看出來只有在 ApiKey 是正確的情況下才能正常調(diào)用 Api
Implement
對于自定義認(rèn)證方式我們之前也有介紹過基于請求頭的認(rèn)證方式,基于 ApiKey 的認(rèn)證和請求頭也類似,
對于自定義認(rèn)證方式,核心需要定義的有兩個類型
一個是認(rèn)證選項,自定義的 AuthenticationOption 一個是認(rèn)證處理器,自定義的認(rèn)證處理邏輯
下面來看實現(xiàn)
ApiKeyAuthenticationOptions
實現(xiàn)如下:
public?sealed?class?ApiKeyAuthenticationOptions?:?AuthenticationSchemeOptions
{
????public?string?ApiKey?{?get;?set;?}
????public?string?ApiKeyName?{?get;?set;?}?=?"X-ApiKey";
????public?string?ClientId?{?get;?set;?}
????public?KeyLocation?KeyLocation?{?get;?set;?}
????public?override?void?Validate()
????{
????????if?(string.IsNullOrWhiteSpace(ApiKey))
????????{
????????????throw?new?ArgumentException("Invalid?ApiKey?configured");
????????}
????}
}
public?enum?KeyLocation
{
????Header?=?0,
????Query?=?1,
????HeaderOrQuery?=?2,
????QueryOrHeader?=?3,
}
繼承于 AuthenticationSchemeOptions 并添加認(rèn)證所需的配置,我們增加了一個 KeyLocation 以配置讀取 ApiKey 的來源,Header 就是從請求頭去讀,Query 就是從查詢字符串讀取,HeaderOrQuery 就是優(yōu)先讀取 Header,對應(yīng)的 QueryOrHeader 就是優(yōu)先讀取查詢字符串
另外我們可以重載 Validate 方法來對我們的配置進(jìn)行校驗
ApiKeyAuthenticationHandler
認(rèn)證處理器是認(rèn)證方式的核心處理邏輯,我們的處理也比較簡單,就是直接讀取 ApiKey 與配置的 ApiKey 對比,一致就認(rèn)證成功,否則就是非法請求,實現(xiàn)如下:
public?sealed?class?ApiKeyAuthenticationHandler?:?AuthenticationHandler<ApiKeyAuthenticationOptions>
{
????public?ApiKeyAuthenticationHandler(IOptionsMonitor?options,?ILoggerFactory?logger,?UrlEncoder?encoder,?ISystemClock?clock )?:?base(options,?logger,?encoder,?clock)
????{
????}
????protected?override?Task?HandleAuthenticateAsync()
????{
????????var?authResult?=?HandleAuthenticateInternal();
????????return?Task.FromResult(authResult);
????}
????private?AuthenticateResult?HandleAuthenticateInternal()
????{
????????StringValues?keyValues;
????????var?keyExists?=?Options.KeyLocation?switch
????????{
????????????KeyLocation.Query?=>?Request.Query.TryGetValue(Options.ApiKeyName,?out?keyValues),
????????????KeyLocation.HeaderOrQuery?=>?Request.Headers.TryGetValue(Options.ApiKeyName,?out?keyValues)?||?Request.Query.TryGetValue(Options.ApiKeyName,?out?keyValues),
????????????KeyLocation.QueryOrHeader?=>?Request.Query.TryGetValue(Options.ApiKeyName,?out?keyValues)?||?Request.Headers.TryGetValue(Options.ApiKeyName,?out?keyValues),
????????????_?=>?Request.Headers.TryGetValue(Options.ApiKeyName,?out?keyValues),
????????};
????????if?(!keyExists)
????????????return?AuthenticateResult.NoResult();
????????if?(keyValues.ToString().Equals(Options.ApiKey))
????????{
????????????var?clientId?=?Options.ClientId.GetValueOrDefault(ApplicationHelper.ApplicationName);
????????????return?AuthenticateResult.Success(
????????????????new?AuthenticationTicket(
????????????????????new?ClaimsPrincipal(new[]
????????????????????{
????????????????????????????new?ClaimsIdentity(new[]
????????????????????????????{
????????????????????????????????new?Claim(nameof(Options.ClientId),?clientId,?ClaimValueTypes.String,?ClaimsIssuer),
????????????????????????????},?Scheme.Name)
????????????????????}),?Scheme.Name)
????????????);
????????}
????????return?AuthenticateResult.Fail("Invalid?Api-Key");
????}
}
Extensions
為了方便使用添加了幾個常用的擴(kuò)展方法,類似于 ASP.NET Core 自帶的認(rèn)證方式
首先增加了一個默認(rèn)的認(rèn)證模式
public?static?class?ApiKeyAuthenticationDefaults
{
????public?const?string?AuthenticationSchema?=?"ApiKey";
}
然后增加了一些依賴注入的擴(kuò)展
public?static?AuthenticationBuilder?AddApiKey(this?AuthenticationBuilder?builder)
{
????return?builder.AddApiKey(ApiKeyAuthenticationDefaults.AuthenticationSchema);
}
public?static?AuthenticationBuilder?AddApiKey(this?AuthenticationBuilder?builder,?string?schema)
{
????return?builder.AddApiKey(schema,?_?=>?{?});
}
public?static?AuthenticationBuilder?AddApiKey(this?AuthenticationBuilder?builder,
????Action?configureOptions )
{
????return?builder.AddApiKey(ApiKeyAuthenticationDefaults.AuthenticationSchema,
????????configureOptions);
}
public?static?AuthenticationBuilder?AddApiKey(this?AuthenticationBuilder?builder,?string?schema,
????Action?configureOptions )
{
????if?(null?!=?configureOptions)
????{
????????builder.Services.Configure(configureOptions);
????}
????return?builder.AddScheme(schema,
????????configureOptions);
}
More
目前的實現(xiàn)比較簡單,只是對 ApiKey 做了校驗,而且是直接讀取的配置,大家可以根據(jù)自己需要進(jìn)行一定的擴(kuò)展,比如 ApiKey 從數(shù)據(jù)庫中讀取、增加額外的 claim 配置等。
對于一些需要配置客戶端訪問權(quán)限的可以使用這種方式來實現(xiàn)簡單的認(rèn)證來代替基于 OAuth 的 Client Credentials?方式,相對來說會簡單一些
References
https://github.com/WeihanLi/WeihanLi.Web.Extensions/blob/dev/src/WeihanLi.Web.Extensions/Authentication/ApiKeyAuthentication/ApiKeyAuthenticationHandler.cs https://github.com/WeihanLi/WeihanLi.Web.Extensions/blob/dev/samples/WeihanLi.Web.Extensions.Samples/Program.cs asp.net core 自定義認(rèn)證方式--請求頭認(rèn)證
