ASP.NET Core 5.0 WebAPI 中的多种身份验证方案
Posted
技术标签:
【中文标题】ASP.NET Core 5.0 WebAPI 中的多种身份验证方案【英文标题】:Multiple authentication schemes in ASP.NET Core 5.0 WebAPI 【发布时间】:2022-01-04 08:19:03 【问题描述】:我有一整套在 .NET 5.0 中开发的 (ASP.NET Core) Web API,并实现了 Cookie 和 OpenIdConnect 身份验证方案。 使用 Azure AD 成功验证(用户 ID 和密码)后,会生成 cookie 并存储用户权限等。
现在,我想使用基于 API 密钥的身份验证(通过请求标头中的 api-key)向第三方消费者公开同一组 API。 我开发了一个自定义身份验证处理程序,如下所示。
using Microsoft.AspNetCore.Authentication;
namespace Management.Deployment.Securities.Authentication
public class ApiKeyAuthenticationSchemeOptions : AuthenticationSchemeOptions
namespace Management.Deployment.Securities.Authentication
public static class ApiKeyAuthenticationDefaults
public static readonly string AuthenticationScheme = "ApiKey";
public static readonly string DisplayName = "ApiKey Authentication Scheme";
ApiKeyAuthenticationHandler 定义如下,直截了当,如果请求标头包含有效的 api 密钥,则添加权限声明(分配给 api 密钥)并将身份验证标记为成功,否则失败。
using Microsoft.AspNetCore.Authentication;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Management.Securities.Authorization;
using System.Security.Claims;
using System.Text.Encodings.Web;
using System.Threading.Tasks;
namespace Management.Deployment.Securities.Authentication
public class ApiKeyAuthenticationHandler : AuthenticationHandler<ApiKeyAuthenticationSchemeOptions>
private const string APIKEY_NAME = "x-api-key";
private const string APIKEY_VALUE = "sdflasuowerposaddfsadf1121234kjdsflkj";
public ApiKeyAuthenticationHandler(IOptionsMonitor<ApiKeyAuthenticationSchemeOptions> options,
ILoggerFactory logger,
UrlEncoder encoder,
ISystemClock clock) : base(options, logger, encoder, clock)
protected override Task<AuthenticateResult> HandleAuthenticateAsync()
string extractedApiKey = Request.Headers[APIKEY_NAME];
if (!APIKEY_VALUE.Equals(extractedApiKey))
return Task.FromResult(AuthenticateResult.Fail("Unauthorized client."));
var claims = new[]
new Claim("Permissions", "23")
;
var claimsIdentity = new ClaimsIdentity(claims, nameof(ApiKeyAuthenticationHandler));
var authenticationTicket = new AuthenticationTicket(new ClaimsPrincipal(claimsIdentity), Scheme.Name);
return Task.FromResult(AuthenticateResult.Success(authenticationTicket));
我还定义了 ApiKeyAuthenticationExtensions 如下。
using Microsoft.AspNetCore.Authentication;
using Management.Deployment.Securities.Authentication;
using System;
namespace Microsoft.Extensions.DependencyInjection
public static class ApiKeyAuthenticationExtensions
public static AuthenticationBuilder AddApiKey(this AuthenticationBuilder builder)
return builder.AddScheme<ApiKeyAuthenticationSchemeOptions, ApiKeyAuthenticationHandler>(ApiKeyAuthenticationDefaults.AuthenticationScheme, ApiKeyAuthenticationDefaults.DisplayName, x => );
public static AuthenticationBuilder AddApiKey(this AuthenticationBuilder builder, Action<ApiKeyAuthenticationSchemeOptions> configureOptions)
return builder.AddScheme<ApiKeyAuthenticationSchemeOptions, ApiKeyAuthenticationHandler>(ApiKeyAuthenticationDefaults.AuthenticationScheme, ApiKeyAuthenticationDefaults.DisplayName, configureOptions);
Startup.cs 中 ConfigureServices() 的略读版本在这里。请注意我使用了 ForwardDefaultSelector。
public void ConfigureServices(IServiceCollection services)
IAuthCookieValidate cookieEvent = new AuthCookieValidate();
services.AddAuthentication(options =>
options.DefaultAuthenticateScheme = CookieAuthenticationDefaults.AuthenticationScheme;
options.DefaultSignInScheme = CookieAuthenticationDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = OpenIdConnectDefaults.AuthenticationScheme;
)
.AddCookie(CookieAuthenticationDefaults.AuthenticationScheme, options =>
options.Cookie.Name = ".Mgnt.AspNetCore.Cookies";
options.ExpireTimeSpan = TimeSpan.FromDays(1);
options.Events = new CookieAuthenticationEvents
OnRedirectToAccessDenied = context =>
context.Response.StatusCode = 403;
return Task.FromResult(0);
,
OnRedirectToLogin = context =>
context.Response.StatusCode = 401;
return Task.FromResult(0);
,
OnValidatePrincipal = cookieEvent.ValidateAsync
;
options.ForwardDefaultSelector = context =>
return context.Request.Headers.ContainsKey(ApiConstants.APIKEY_NAME) ? ApiKeyAuthenticationDefaults.AuthenticationScheme : CookieAuthenticationDefaults.AuthenticationScheme;
;
options.Cookie.HttpOnly = true;
options.Cookie.SecurePolicy = CookieSecurePolicy.Always;
)
.AddOpenIdConnect(OpenIdConnectDefaults.AuthenticationScheme, options => Configuration.Bind(OpenIdConnectDefaults.AuthenticationScheme, options))
.AddApiKey();
services.Configure<OpenIdConnectOptions>(OpenIdConnectDefaults.AuthenticationScheme, options =>
options.Scope.Add("openid");
options.Scope.Add("profile");
options.Scope.Add("email");
);
services.AddSingleton<IAuthorizationPolicyProvider, AuthorizationPolicyProvider>();
services.AddSingleton<IAuthorizationHandler, PermissionHandler>();
配置方法如下。
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
if (env.IsDevelopment())
app.UseDeveloperExceptionPage();
app.UseHsts();
app.Use((context, next) =>
context.Request.Scheme = "https";
return next();
);
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
endpoints.MapControllers();
);
当我在请求标头中发送正确的 apikey 时,自定义处理程序将返回成功作为身份验证结果,并进一步处理请求。
但如果传递了错误的 api 密钥,它不会返回身份验证失败消息 - “未经授权的客户端。”。而是进一步处理请求并发送附加的响应。
为解决此问题需要进行哪些更改,以便 api 返回身份验证失败消息 - “未经授权的客户端”。并停止进一步处理请求?
【问题讨论】:
【参考方案1】:如果您打算使用 apikeys,那么您只能靠自己,并且(据我所知)没有内置的对 API-keys 的直接支持。但是,内置了对基于 JWT 的访问令牌的支持,我建议您也将它用于想要访问您的 api 的外部第三方。也许使用客户端凭据流。
如需帮助,请参阅http://codingsonata.com/secure-asp-net-core-web-api-using-api-key-authentication/
我还认为您应该配置并让授权处理程序负责决定谁可以访问服务。
见Policy-based authorization in ASP.NET Core
【讨论】:
我已经用实施细节更新了我的问题。请看一看。 查看我的更新答案以上是关于ASP.NET Core 5.0 WebAPI 中的多种身份验证方案的主要内容,如果未能解决你的问题,请参考以下文章
[WebApi]ASP.Net Core 中使用JWT认证(3.1版本,5.0也可以使用)
在 ASP.NET Core 5.0 Web API 中实现 DelegatingHandler?
如何按照存储库模式在 Asp.Net Core 5.0 项目上实现 .Net Core Identity?
ASP.NET Core 5.0中的Host.CreateDefaultBuilder执行过程