代码改变世界
[登录 · 注册]
  • 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, ApiKeyAuthenticationHandler>(
                ApiKeyAuthenticationOptions.DefaultScheme,
                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()
    {
        //...
    }
    

    搞定。

  • 上一篇:.NET Core 中的命名问题:Startup 中的 ConfigureServices 与 Configure
    下一篇:.NET Core 中读取 Request.Headers 的姿势
  • 【推广】 阿里云小站-上云优惠聚集地(新老客户同享)更有每天限时秒杀!
    【推广】 云服务器低至0.95折 1核2G ECS云服务器8.1元/月
    【推广】 阿里云老用户升级四重礼遇享6.5折限时折扣!
  • 原文:https://www.cnblogs.com/dudu/p/11702713.html
走看看 - 开发者的网上家园