.Net Core 自定义身份验证使用 API 密钥和 Identity Server 4

Posted

技术标签:

【中文标题】.Net Core 自定义身份验证使用 API 密钥和 Identity Server 4【英文标题】:.Net Core Custom Authentication using API Keys with Identity Server 4 【发布时间】:2019-08-04 17:06:44 【问题描述】:

我有一个使用 JWT 令牌进行身份验证的 .NET Core 2.2 Web API。令牌由 Identity Server 4 在单独的 API 上生成。

所有身份验证和授权都可以使用 JWT 令牌按预期工作。但我需要扩展它以允许使用 API 密钥。如果提供了 API 密钥,我想加载该特定用户的声明,将其添加到请求中并让 Authorize 属性处理设置的策略。

这是我根据here 的建议到目前为止所做的事情。我的错误与链接的帖子完全相同,它也适用于我使用具有一组角色的 GenericPrincipal 但我使用的是 AuthorisationPolicies 并且我当前的实现总是收到 401 错误,给我类似于上面链接的错误。

Startup.cs

public void ConfigureServices(IServiceCollection services)
    
        services.AddMvcCore(options =>
        
            options.Filters.Add(new RequireHttpsAttribute());
            options.Filters.Add(new AuthorizeFilter());
            options.Filters.Add(typeof(ValidateModelStateAttribute));
            options.AllowEmptyInputInBodyModelBinding = true;
        )
        .AddAuthorization(options =>
        
            options.AddPolicies();
        )
        .AddJsonFormatters();

        services.AddAuthentication(IdentityServerAuthenticationDefaults.AuthenticationScheme)
            .AddIdentityServerAuthentication(options =>
            
                options.Authority = Configuration["Authentication:Authority"];
                options.RequireHttpsMetadata = true;
                options.ApiName = Configuration["Authentication:ApiName"];
            );
        services.AddCors();
    

public void Configure(IApplicationBuilder app, IHostingEnvironment env)
    
        if (env.IsDevelopment())
        
            app.UseDeveloperExceptionPage();
        
        else
        
            // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
            app.UseHsts();
        

        app.UseCors(policy =>
        
            policy.AllowAnyHeader();
            policy.AllowAnyMethod();
            policy.AllowAnyOrigin();
        );

        app.UseHttpsRedirection();
        app.UseMiddleware<ApiKeyMiddleware>();
        app.UseAuthentication();
        app.UseMvc();
    

AuthorizationPolicies.cs

public static class AuthorizationPolicies

    public const string ReadUsersPolicy = "ReadUsers";
    public const string EditUsersPolicy = "EditUsers";

    public static void AddPolicies(this AuthorizationOptions options)
    
        options.AddPolicy(ReadUsersPolicy, policy => policy.RequireClaim(Foo.Permission, Foo.CanReadUsers));
        options.AddPolicy(EditUsersPolicy, policy => policy.RequireClaim(Foo.Permission, Foo.CanEditUsers));
    

ApiKeyMiddleware

public class ApiKeyMiddleware

    public ApiKeyMiddleware(RequestDelegate next)
    
        _next = next;
    
    private readonly RequestDelegate _next;

    public async Task Invoke(HttpContext context)
    
        if (context.Request.Path.StartsWithSegments(new PathString("/api")))
        
            if (context.Request.Headers.Keys.Contains("ApiKey", StringComparer.InvariantCultureIgnoreCase))
            
                var headerKey = context.Request.Headers["ApiKey"].FirstOrDefault();
                await ValidateApiKey(context, _next, headerKey);
            
            else
            
                await _next.Invoke(context);
            
        
        else
        
            await _next.Invoke(context);
        
    

    private async Task ValidateApiKey(HttpContext context, RequestDelegate next, string key)
    
        var userClaimsService = context.RequestServices.GetService<IUserClaimsService>();
        List<string> permissions = (await userClaimsService.GetAllPermissionsForApiKey(key))?.ToList();
        if (permissions == null)
        
            context.Response.StatusCode = (int)HttpStatusCode.Unauthorized;
            await context.Response.WriteAsync("Invalid Api Key");
            return;
        

        ICollection<Claim> claims = permissions.Select(x => new Claim(FooClaimTypes.Permission, x)).ToList();
        var identity = new ClaimsIdentity(claims);
        var principal = new ClaimsPrincipal(identity);
        context.User = principal;
        await next.Invoke(context);
    

UsersController.cs

[Authorize(AuthorizationPolicies.EditUsersPolicy)]
    public async Task<IActionResult> Put([FromBody] UserUpdateDto userUpdateDto)
    
        ...
    

【问题讨论】:

ValidateApiKey 函数中放置一个断点并检查您是否创建了具有正确声明以满足您的策略的主体。 是的,委托人对特定保单拥有权利主张。如果没有,我希望它会抛出 403。但是主体上的 isAuthenticated() 返回 false,我认为这会导致 401。 【参考方案1】:

显然,我必须在ClaimsIdentity 上将AuthenticationType 设置为自定义,如here 所述。

var identity = new ClaimsIdentity(claims, "Custom");

【讨论】:

以上是关于.Net Core 自定义身份验证使用 API 密钥和 Identity Server 4的主要内容,如果未能解决你的问题,请参考以下文章

创建可由 NET Core API 使用的自定义 cookie

ASP.Net-Core 中的自定义身份验证

如何从 ASP.NET Core 2.0 中的自定义中间件请求身份验证

ASP Core 3.0 API 令牌自定义令牌身份验证(不是 jwt!)

在身份验证失败时返回自定义状态码 .Net Core

带有 Windows 身份验证的 ASP.NET Core 2.1 自定义 RoleProvider