【问题标题】:How to create a ApiKey authentication scheme for .NET Core如何为 .NET Core 创建 ApiKey 身份验证方案
【发布时间】:2022-09-24 05:01:19
【问题描述】:

我试图将 ApiKey 身份验证方案设置为我的 Api,但找不到任何关于它的帖子或文档。

我发现更接近的是this Microsoft page,但没有说明如何注册身份验证处理程序。

我正在使用.Net Core 6。

    标签: authentication .net-core asp.net-core-webapi


    【解决方案1】:

    找到了一个主要基于这个伟大的 Joonas Westlin guide to implement basic authentication scheme 的解决方案。学分应该归他所有。

    脚步:

    1. Implement the options class inheriting from `AuthenticationSchemeOptions` and other boiler classes that will be need after.
    2. Create the handler, inherit from `AuthenticationHandler<TOptions>`    
    3. Override handler methods `HandleAuthenticateAsync` to get the key and call your implementation of `IApiKeyAuthenticationService` 
    4. Register the scheme with `AddScheme<TOptions, THandler>(string, Action<TOptions>)` on the `AuthenticationBuilder`, which you get by calling `AddAuthentication` on the service collection
    5. Implement the `IApiKeyAuthenticationService` and add it to Service Collection.
    

    这里所有的代码。 AuthenticationSchemeOptions 和其他锅炉类:

    //the Service interface for the service that will get the key to validate against some store
    public interface IApiKeyAuthenticationService
    {
        Task<bool> IsValidAsync(string apiKey);
    }
    //the class for defaults following the similar to .Net Core JwtBearerDefaults class
    public static class ApiKeyAuthenticationDefaults
    {
        public const string AuthenticationScheme = "ApiKey";
    }
    
    public class ApiKeyAuthenticationOptions : AuthenticationSchemeOptions{}; //Nothing to do
    
    public class ApiKeyAuthenticationPostConfigureOptions : IPostConfigureOptions<ApiKeyAuthenticationOptions>
    {
        public void PostConfigure(string name, ApiKeyAuthenticationOptions options){} //Nothing to do
    };
    

    处理程序:

    public class ApiKeyAuthenticationHandler : AuthenticationHandler<ApiKeyAuthenticationOptions>
    {
        private const string AuthorizationHeaderName = "Authorization";
        private const string ApiKeySchemeName = ApiKeyAuthenticationDefaults.AuthenticationScheme;
        private readonly IApiKeyAuthenticationService _authenticationService;
    
        public ApiKeyAuthenticationHandler(
            IOptionsMonitor<ApiKeyAuthenticationOptions> options,
            ILoggerFactory logger,
            UrlEncoder encoder,
            ISystemClock clock,
            IApiKeyAuthenticationService authenticationService)
            : base(options, logger, encoder, clock)
        {
            _authenticationService = authenticationService;
        }
    
        protected override async Task<AuthenticateResult> HandleAuthenticateAsync()
        {
            if (!Request.Headers.ContainsKey(AuthorizationHeaderName))
            {
                //Authorization header not in request
                return AuthenticateResult.NoResult();
            }
    
            if (!AuthenticationHeaderValue.TryParse(Request.Headers[AuthorizationHeaderName], out AuthenticationHeaderValue? headerValue))
            {
                //Invalid Authorization header
                return AuthenticateResult.NoResult();
            }
    
            if (!ApiKeySchemeName.Equals(headerValue.Scheme, StringComparison.OrdinalIgnoreCase))
            {
                //Not ApiKey authentication header
                return AuthenticateResult.NoResult();
            }
            if ( headerValue.Parameter is null)
            {
                //Missing key
                return AuthenticateResult.Fail("Missing apiKey");
            }            
    
            bool isValid = await _authenticationService.IsValidAsync(headerValue.Parameter);
    
            if (!isValid)
            {
                return AuthenticateResult.Fail("Invalid apiKey");
            }
            var claims = new[] { new Claim(ClaimTypes.Name, "Service") };            
            var identity = new ClaimsIdentity(claims, Scheme.Name);
            var principal = new ClaimsPrincipal(identity);
            var ticket = new AuthenticationTicket(principal, Scheme.Name);
            return AuthenticateResult.Success(ticket);
        }
    
        protected override async Task HandleChallengeAsync(AuthenticationProperties properties)
        {
            Response.Headers["WWW-Authenticate"] = $"ApiKey \", charset=\"UTF-8\"";
            await base.HandleChallengeAsync(properties);
        }
    }
    

    AuthenticationBuilder 类的扩展,以方便注册 ApiKey 方案:

    public static class ApiKeyAuthenticationExtensions
    {
        public static AuthenticationBuilder AddApiKey<TAuthService>(this AuthenticationBuilder builder)
            where TAuthService : class, IApiKeyAuthenticationService
        {
            return AddApiKey<TAuthService>(builder, ApiKeyAuthenticationDefaults.AuthenticationScheme, _ => { });
        }
    
        public static AuthenticationBuilder AddApiKey<TAuthService>(this AuthenticationBuilder builder, string authenticationScheme)
            where TAuthService : class, IApiKeyAuthenticationService
        {
            return AddApiKey<TAuthService>(builder, authenticationScheme, _ => { });
        }
    
        public static AuthenticationBuilder AddApiKey<TAuthService>(this AuthenticationBuilder builder, Action<ApiKeyAuthenticationOptions> configureOptions)
            where TAuthService : class, IApiKeyAuthenticationService
        {
            return AddApiKey<TAuthService>(builder, ApiKeyAuthenticationDefaults.AuthenticationScheme, configureOptions);
        }
    
        public static AuthenticationBuilder AddApiKey<TAuthService>(this AuthenticationBuilder builder, string authenticationScheme, Action<ApiKeyAuthenticationOptions> configureOptions)
            where TAuthService : class, IApiKeyAuthenticationService
        {
            builder.Services.AddSingleton<IPostConfigureOptions<ApiKeyAuthenticationOptions>, ApiKeyAuthenticationPostConfigureOptions>();
            builder.Services.AddTransient<IApiKeyAuthenticationService, TAuthService>();
    
            return builder.AddScheme<ApiKeyAuthenticationOptions, ApiKeyAuthenticationHandler>(
                authenticationScheme, configureOptions);
        }
    }
    

    实施 Authentication 服务以根据您的配置文件或其他存储从请求标头验证密钥:

    public class ApiKeyAuthenticationService : IApiKeyAuthenticationService
    {
        public Task<bool> IsValidAsync(string apiKey)
        {
            //Write your validation code here
            return Task.FromResult(apiKey == "Test");
        }
    }
    

    现在,要使用它,只需在开头添加以下代码:

    //register the schema
    builder.Services.AddAuthentication(ApiKeyAuthenticationDefaults.AuthenticationScheme)
      .AddScheme<ApiKeyAuthenticationOptions, ApiKeyAuthenticationHandler>(ApiKeyAuthenticationDefaults.AuthenticationScheme, null);
    
    //Register the Authentication service Handler that will be consumed by the handler.
    builder.Services.AddSingleton<IApiKeyAuthenticationService,ApiKeyAuthenticationService>();
    

    或者,以更优雅的方式,使用扩展:

    builder.Services
    .AddAuthentication(ApiKeyAuthenticationDefaults.AuthenticationScheme)
    .AddApiKey<ApiKeyAuthenticationService>();
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2021-10-18
      • 2018-02-23
      • 2021-08-27
      • 2018-03-23
      • 2018-09-22
      • 2018-12-17
      • 2020-10-28
      • 2020-10-15
      相关资源
      最近更新 更多