Net Core 2.1 APIプロジェクトがあります。リクエストヘッダーを使用してAPIキーを取得し、データベースに対してチェックして、期待されるキーの1つと一致するかどうかを確認します。許可されている場合、リクエストの続行を許可します。許可されていない場合、不正な応答を送り返します。
startup.cs
services.AddAuthorization(options =>
{
options.AddPolicy("APIKeyAuth", policyCorrectUser =>
{
policyCorrectUser.Requirements.Add(new APIKeyAuthReq());
});
});
services.AddSingleton<Microsoft.AspNetCore.Authorization.IAuthorizationHandler, APIKeyAuthHandler>();
APIKeyAuthHandler.cs
public class APIKeyAuthReq : IAuthorizationRequirement { }
public class APIKeyAuthHandler : AuthorizationHandler<APIKeyAuthReq>
{
protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, APIKeyAuthReq requirement)
{
if (context == null)
throw new ArgumentNullException(nameof(context));
if (requirement == null)
throw new ArgumentNullException(nameof(requirement));
var httpContext = context.Resource as Microsoft.AspNetCore.Mvc.Filters.AuthorizationFilterContext;
var headers = httpContext.HttpContext.Request.Headers;
if (headers.TryGetValue("Authorization", out Microsoft.Extensions.Primitives.StringValues value))
{
using (DBContext db = new DBContext ())
{
var token = value.First().Split(" ")[1];
var login = db.Login.FirstOrDefault(l => l.Apikey == token);
if (login == null)
{
context.Fail();
httpContext.HttpContext.Response.StatusCode = 403;
return Task.CompletedTask;
} else
{
httpContext.HttpContext.Items.Add("CurrentUser", login);
context.Succeed(requirement);
return Task.CompletedTask;
}
}
}
}
}
そして、controller.cs
[Route("api/[controller]/[action]")]
[Authorize("APIKeyAuth")]
[ApiController]
public class SomeController : ControllerBase
{
}
有効なキーが存在する場合はすべて正常に動作しますが、存在しない場合は、403の代わりにNo authenticationSchemeに対して500内部エラーがスローされます。
Net Core(Net Framework/Forms Authenticationに由来)は比較的新しいので、この種の認証を行うより正確な方法があれば、お知らせください。
エラーメッセージ:
InvalidOperationException:authenticationSchemeが指定されておらず、DefaultChallengeSchemeが見つかりませんでした。 Microsoft.AspNetCore.Authentication.AuthenticationService.ChallengeAsync(HttpContextコンテキスト、文字列スキーム、AuthenticationPropertiesプロパティ)
トークンベースの認証が推奨されます。ただし、カスタムApiKeyAuth
スキームが必要な場合は、可能です。
まず、承認前にユーザーを認証する必要があるため、Authorize("APIKeyAuth")
は意味をなさないようです。着信要求がある場合、サーバーは使用が誰であるかわかりません。 ApiKeyAuth
をAuthorization
からAuthentication
に移動しましょう。
それを行うには、オプションを保持するために使用できるダミーApiKeyAuthOpts
を作成するだけです
public class ApiKeyAuthOpts : AuthenticationSchemeOptions
{
}
認証を処理するための簡単なApiKeyAuthHandler
(上記のコードの一部をコピーするだけです)::
public class ApiKeyAuthHandler : AuthenticationHandler<ApiKeyAuthOpts>
{
public ApiKeyAuthHandler(IOptionsMonitor<ApiKeyAuthOpts> options, ILoggerFactory logger, UrlEncoder encoder, ISystemClock clock)
: base(options, logger, encoder, clock)
{
}
private const string API_TOKEN_PREFIX = "api-key";
protected override async Task<AuthenticateResult> HandleAuthenticateAsync()
{
string token = null;
string authorization = Request.Headers["Authorization"];
if (string.IsNullOrEmpty(authorization)) {
return AuthenticateResult.NoResult();
}
if (authorization.StartsWith(API_TOKEN_PREFIX, StringComparison.OrdinalIgnoreCase)) {
token = authorization.Substring(API_TOKEN_PREFIX.Length).Trim();
}
if (string.IsNullOrEmpty(token)) {
return AuthenticateResult.NoResult();
}
// does the token match ?
bool res =false;
using (DBContext db = new DBContext()) {
var login = db.Login.FirstOrDefault(l => l.Apikey == token); // query db
res = login ==null ? false : true ;
}
if (!res) {
return AuthenticateResult.Fail($"token {API_TOKEN_PREFIX} not match");
}
else {
var id=new ClaimsIdentity(
new Claim[] { new Claim("Key", token) }, // not safe , just as an example , should custom claims on your own
Scheme.Name
);
ClaimsPrincipal principal=new ClaimsPrincipal( id);
var ticket = new AuthenticationTicket(principal, new AuthenticationProperties(), Scheme.Name);
return AuthenticateResult.Success(ticket);
}
}
}
最後に、それらを機能させるには、まだ少し設定が必要です。
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
services.AddAuthentication("ApiKeyAuth")
.AddScheme<ApiKeyAuthOpts,ApiKeyAuthHandler>("ApiKeyAuth","ApiKeyAuth",opts=>{ });
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
// ...
app.UseAuthentication();
app.UseHttpsRedirection();
app.UseMvc();
}
[Authorize]
で保護されたアクションメソッドにリクエストを送信すると:
GET https://localhost:44366/api/values/1 HTTP/1.1
Authorization: api-key xxx_yyy_zzz
応答はHTTP/1.1 200 OK
になります。正しいキーなしでリクエストを送信すると、レスポンスは次のようになります。
HTTP/1.1 401 Unauthorized
Server: Kestrel
X-SourceFiles: =?UTF-8?B?RDpccmVwb3J0XDIwMThcOVw5LTEyXFNPLkFwaUtleUF1dGhcQXBwXEFwcFxhcGlcdmFsdWVzXDE=?=
X-Powered-By: ASP.NET
Date: Wed, 12 Sep 2018 08:33:23 GMT
Content-Length: 0