'UseAuthentication()' 到底是做什么用的?
What exactly is 'UseAuthentication()' for?
我有一个关于 ASP.NET 核心 2 中身份验证的问题:调用 app.UseAuthentication() 到底是为了什么?
这是我可以实施自定义身份验证逻辑的基本先决条件吗?我已经看过 UseAuthentication and also of the actual middleware AuthenticationMiddleware 的实现,但老实说,我不明白它实际在做什么以及为什么有必要这样做。
换句话说:
我需要调用 UseAuthentication()
或者这是一个很好的选择,我仍然可以进行自定义身份验证?
如果我不调用 UseAuthentication() 就没事,我仍然会对 AuthenticationMiddleware 实际在做什么感兴趣。因此,如果您知道这一点,如果您也能为我解释一下,我将不胜感激。
你确实需要调用它。
UseAuthentication()
记录为:
Adds the AuthenticationMiddleware to the specified IApplicationBuilder, which enables authentication capabilities.
它基本上是这样做的:
IApplicationBuilder AddAuthentication(this IApplicationBuilder app) {
return app.UseMiddleware<AuthenticationMiddleware>();
}
...所以它只是为您节省了一些输入和可能的一些额外的 using
导入。
这会将一个 AuthenticationMiddleware
实例添加到进程的 request-handling 管道中,并且此特定对象会添加用于身份验证的管道。
如果您编写自定义中间件(就像您在示例中所做的那样),则不需要调用 AddAuthentication
,因为身份验证中间件不会知道您自己的中间件。
也就是说,您可能不想创建自己的中间件:您可能想创建一个新的身份验证处理程序,它可以很好地与 ASP.NET 身份验证框架配合使用(以便您使用 [Authorize]
控制器上的属性)。
要创建自定义身份验证,您必须创建一个继承自 AuthenticationHandler
的专用处理程序,并实现相关方法。您可以查看 github 上的基本身份验证示例:https://github.com/blowdart/idunno.Authentication,但这里有一个快速示例来显示自定义处理程序的要点。
public class BasicAuthenticationOptions : AuthenticationSchemeOptions
{
public BasicAuthenticationOptions()
{
}
}
internal class BasicAuthenticationHandler : AuthenticationHandler<BasicAuthenticationOptions>
{
private const string _Scheme = "MyScheme";
public BasicAuthenticationHandler(
IOptionsMonitor<BasicAuthenticationOptions> options,
ILoggerFactory logger,
UrlEncoder encoder,
ISystemClock clock) : base(options, logger, encoder, clock)
{
}
protected override async Task<AuthenticateResult> HandleAuthenticateAsync()
{
string authorizationHeader = Request.Headers["Custom-Auth-Handler"];
// create a ClaimsPrincipal from your header
var claims = new[]
{
new Claim(ClaimTypes.NameIdentifier, "My Name")
};
var claimsPrincipal = new ClaimsPrincipal(new ClaimsIdentity(claims, Scheme.Name));
var ticket = new AuthenticationTicket(claimsPrincipal,
new AuthenticationProperties { IsPersistent = false },
Scheme.Name
);
return AuthenticateResult.Success(ticket);
}
}
然后您可以在 Startup.cs
中注册您的新方案:
public void ConfigureServices(IServiceCollection services)
{
services
.AddAuthentication(BasicAuthenticationDefaults.AuthenticationScheme)
.AddScheme<BasicAuthenticationOptions, BasicAuthenticationHandler>("MyScheme", options => { /* configure options */ })
}
虽然这是一个旧线程,但由于我最近偶然遇到了同样的问题,我认为对内部结构进行更多阐述可能会对其他人有所帮助
简短的回答取决于您的服务类型和您的 API。您 不需要 在以下情况下调用 UseAuthentication
:
- 您实现自己的处理身份验证的中间件 - 无需在此详细说明。你自己处理一切,显然不需要额外的依赖
- 您不需要自动或远程身份验证
远程认证
需要重定向到身份提供者的身份验证,例如 OpenID Connect。
是什么让它如此特别?
这些中间件需要关联不同的 http 调用。
初始调用首先由中间件处理,然后重定向到身份提供者(用户需要登录的地方),然后返回到中间件。
在这种情况下,中间件需要拥有请求并且不允许其他身份验证中间件参与该过程。
这是 middleware 代码的第一部分:
// Give any IAuthenticationRequestHandler schemes a chance to handle the request
var handlers = context.RequestServices.GetRequiredService<IAuthenticationHandlerProvider>();
foreach (var scheme in await Schemes.GetRequestHandlerSchemesAsync())
{
var handler = await handlers.GetHandlerAsync(context, scheme.Name) as
IAuthenticationRequestHandler;
if (handler != null && await handler.HandleRequestAsync())
{
return;
}
}
- 这当然是一个简化的解释,因为远程处理程序更复杂。目标最终是集中和解释中间件的行为
自动认证
运行 自动为默认方案进行身份验证。顾名思义,如果您定义了默认身份验证方案,那么与中间件关联的身份验证处理程序将始终 运行.
凭直觉,您会期望身份验证中间件首先 运行,特别是它们应该 运行 在 MVC 层(即控制器)之前。但是,这也意味着身份验证层不知道应该 运行 哪些控制器或这些控制器的授权要求,换句话说,它不知道授权策略是什么 [Authorize("Policy")]
它应该评估。
所以从逻辑上讲,我们想先评估策略,然后才 运行 验证逻辑。这就是为什么身份验证处理程序移入 ASP 2.* 成为通用服务而不与中间件耦合的原因。
但是,在某些情况下,无论您的策略如何,您总是希望身份验证处理程序 运行。在这种情况下,您可以定义默认身份验证方案,该方案将自动 运行.
这解释了 middleware 代码的第二部分:
var defaultAuthenticate = await Schemes.GetDefaultAuthenticateSchemeAsync();
if (defaultAuthenticate != null)
{
var result = await context.AuthenticateAsync(defaultAuthenticate.Name);
if (result?.Principal != null)
{
context.User = result.Principal;
}
}
如果您正在开发支持多种身份验证方案的 REST API 或者混合使用经过身份验证和未经过身份验证的控制器,那么您不需要自动身份验证,因为它增加了冗余。
结论
这给我们带来了有趣的问题和答案:当身份验证不是自动且非远程时,何时何地进行身份验证?
在正常的 MVC 授权流程中,这发生在调用 IAuthenticationService.AuthenticateAsync
的 AuthorizeFilter class 中
- 如果您实现自己的授权层或使用较低级别的 APIs(例如未作为控制器实现的 websockets),您可以自己调用此方法
对于这些情况,不需要调用 UseAuthentication
来自 UseAuthentication
的 GitHub 源代码。
public static IApplicationBuilder UseAuthentication(this IApplicationBuilder app)
{
if (app == null)
{
throw new ArgumentNullException(nameof(app));
}
return app.UseMiddleware<AuthenticationMiddleware>();
}
如您所见,它只是添加了一个名为 AuthenticationMiddleware
.
的中间件
这正是 AuthenticationMiddleware
正在做的事情:
public async Task Invoke(HttpContext context)
{
context.Features.Set<IAuthenticationFeature>(new AuthenticationFeature
{
OriginalPath = context.Request.Path,
OriginalPathBase = context.Request.PathBase
});
// Give any IAuthenticationRequestHandler schemes a chance to handle the request
var handlers = context.RequestServices.GetRequiredService<IAuthenticationHandlerProvider>();
foreach (var scheme in await Schemes.GetRequestHandlerSchemesAsync())
{
var handler = await handlers.GetHandlerAsync(context, scheme.Name) as IAuthenticationRequestHandler;
if (handler != null && await handler.HandleRequestAsync())
{
return;
}
}
var defaultAuthenticate = await Schemes.GetDefaultAuthenticateSchemeAsync();
if (defaultAuthenticate != null)
{
var result = await context.AuthenticateAsync(defaultAuthenticate.Name);
if (result?.Principal != null)
{
context.User = result.Principal;
}
if (result?.Succeeded ?? false)
{
var authFeatures = new AuthenticationFeatures(result);
context.Features.Set<IHttpAuthenticationFeature>(authFeatures);
context.Features.Set<IAuthenticateResultFeature>(authFeatures);
}
}
await _next(context);
}
我有一个关于 ASP.NET 核心 2 中身份验证的问题:调用 app.UseAuthentication() 到底是为了什么?
这是我可以实施自定义身份验证逻辑的基本先决条件吗?我已经看过 UseAuthentication and also of the actual middleware AuthenticationMiddleware 的实现,但老实说,我不明白它实际在做什么以及为什么有必要这样做。
换句话说:
我需要调用 UseAuthentication()
或者这是一个很好的选择,我仍然可以进行自定义身份验证?
如果我不调用 UseAuthentication() 就没事,我仍然会对 AuthenticationMiddleware 实际在做什么感兴趣。因此,如果您知道这一点,如果您也能为我解释一下,我将不胜感激。
你确实需要调用它。
UseAuthentication()
记录为:
Adds the AuthenticationMiddleware to the specified IApplicationBuilder, which enables authentication capabilities.
它基本上是这样做的:
IApplicationBuilder AddAuthentication(this IApplicationBuilder app) {
return app.UseMiddleware<AuthenticationMiddleware>();
}
...所以它只是为您节省了一些输入和可能的一些额外的 using
导入。
这会将一个 AuthenticationMiddleware
实例添加到进程的 request-handling 管道中,并且此特定对象会添加用于身份验证的管道。
如果您编写自定义中间件(就像您在示例中所做的那样),则不需要调用 AddAuthentication
,因为身份验证中间件不会知道您自己的中间件。
也就是说,您可能不想创建自己的中间件:您可能想创建一个新的身份验证处理程序,它可以很好地与 ASP.NET 身份验证框架配合使用(以便您使用 [Authorize]
控制器上的属性)。
要创建自定义身份验证,您必须创建一个继承自 AuthenticationHandler
的专用处理程序,并实现相关方法。您可以查看 github 上的基本身份验证示例:https://github.com/blowdart/idunno.Authentication,但这里有一个快速示例来显示自定义处理程序的要点。
public class BasicAuthenticationOptions : AuthenticationSchemeOptions
{
public BasicAuthenticationOptions()
{
}
}
internal class BasicAuthenticationHandler : AuthenticationHandler<BasicAuthenticationOptions>
{
private const string _Scheme = "MyScheme";
public BasicAuthenticationHandler(
IOptionsMonitor<BasicAuthenticationOptions> options,
ILoggerFactory logger,
UrlEncoder encoder,
ISystemClock clock) : base(options, logger, encoder, clock)
{
}
protected override async Task<AuthenticateResult> HandleAuthenticateAsync()
{
string authorizationHeader = Request.Headers["Custom-Auth-Handler"];
// create a ClaimsPrincipal from your header
var claims = new[]
{
new Claim(ClaimTypes.NameIdentifier, "My Name")
};
var claimsPrincipal = new ClaimsPrincipal(new ClaimsIdentity(claims, Scheme.Name));
var ticket = new AuthenticationTicket(claimsPrincipal,
new AuthenticationProperties { IsPersistent = false },
Scheme.Name
);
return AuthenticateResult.Success(ticket);
}
}
然后您可以在 Startup.cs
中注册您的新方案:
public void ConfigureServices(IServiceCollection services)
{
services
.AddAuthentication(BasicAuthenticationDefaults.AuthenticationScheme)
.AddScheme<BasicAuthenticationOptions, BasicAuthenticationHandler>("MyScheme", options => { /* configure options */ })
}
虽然这是一个旧线程,但由于我最近偶然遇到了同样的问题,我认为对内部结构进行更多阐述可能会对其他人有所帮助
简短的回答取决于您的服务类型和您的 API。您 不需要 在以下情况下调用 UseAuthentication
:
- 您实现自己的处理身份验证的中间件 - 无需在此详细说明。你自己处理一切,显然不需要额外的依赖
- 您不需要自动或远程身份验证
远程认证
需要重定向到身份提供者的身份验证,例如 OpenID Connect。
是什么让它如此特别?
这些中间件需要关联不同的 http 调用。
初始调用首先由中间件处理,然后重定向到身份提供者(用户需要登录的地方),然后返回到中间件。 在这种情况下,中间件需要拥有请求并且不允许其他身份验证中间件参与该过程。
这是 middleware 代码的第一部分:
// Give any IAuthenticationRequestHandler schemes a chance to handle the request
var handlers = context.RequestServices.GetRequiredService<IAuthenticationHandlerProvider>();
foreach (var scheme in await Schemes.GetRequestHandlerSchemesAsync())
{
var handler = await handlers.GetHandlerAsync(context, scheme.Name) as
IAuthenticationRequestHandler;
if (handler != null && await handler.HandleRequestAsync())
{
return;
}
}
- 这当然是一个简化的解释,因为远程处理程序更复杂。目标最终是集中和解释中间件的行为
自动认证
运行 自动为默认方案进行身份验证。顾名思义,如果您定义了默认身份验证方案,那么与中间件关联的身份验证处理程序将始终 运行.
凭直觉,您会期望身份验证中间件首先 运行,特别是它们应该 运行 在 MVC 层(即控制器)之前。但是,这也意味着身份验证层不知道应该 运行 哪些控制器或这些控制器的授权要求,换句话说,它不知道授权策略是什么 [Authorize("Policy")]
它应该评估。
所以从逻辑上讲,我们想先评估策略,然后才 运行 验证逻辑。这就是为什么身份验证处理程序移入 ASP 2.* 成为通用服务而不与中间件耦合的原因。
但是,在某些情况下,无论您的策略如何,您总是希望身份验证处理程序 运行。在这种情况下,您可以定义默认身份验证方案,该方案将自动 运行.
这解释了 middleware 代码的第二部分:
var defaultAuthenticate = await Schemes.GetDefaultAuthenticateSchemeAsync();
if (defaultAuthenticate != null)
{
var result = await context.AuthenticateAsync(defaultAuthenticate.Name);
if (result?.Principal != null)
{
context.User = result.Principal;
}
}
如果您正在开发支持多种身份验证方案的 REST API 或者混合使用经过身份验证和未经过身份验证的控制器,那么您不需要自动身份验证,因为它增加了冗余。
结论
这给我们带来了有趣的问题和答案:当身份验证不是自动且非远程时,何时何地进行身份验证?
在正常的 MVC 授权流程中,这发生在调用 IAuthenticationService.AuthenticateAsync
的 AuthorizeFilter class 中- 如果您实现自己的授权层或使用较低级别的 APIs(例如未作为控制器实现的 websockets),您可以自己调用此方法
对于这些情况,不需要调用 UseAuthentication
来自 UseAuthentication
的 GitHub 源代码。
public static IApplicationBuilder UseAuthentication(this IApplicationBuilder app)
{
if (app == null)
{
throw new ArgumentNullException(nameof(app));
}
return app.UseMiddleware<AuthenticationMiddleware>();
}
如您所见,它只是添加了一个名为 AuthenticationMiddleware
.
这正是 AuthenticationMiddleware
正在做的事情:
public async Task Invoke(HttpContext context)
{
context.Features.Set<IAuthenticationFeature>(new AuthenticationFeature
{
OriginalPath = context.Request.Path,
OriginalPathBase = context.Request.PathBase
});
// Give any IAuthenticationRequestHandler schemes a chance to handle the request
var handlers = context.RequestServices.GetRequiredService<IAuthenticationHandlerProvider>();
foreach (var scheme in await Schemes.GetRequestHandlerSchemesAsync())
{
var handler = await handlers.GetHandlerAsync(context, scheme.Name) as IAuthenticationRequestHandler;
if (handler != null && await handler.HandleRequestAsync())
{
return;
}
}
var defaultAuthenticate = await Schemes.GetDefaultAuthenticateSchemeAsync();
if (defaultAuthenticate != null)
{
var result = await context.AuthenticateAsync(defaultAuthenticate.Name);
if (result?.Principal != null)
{
context.User = result.Principal;
}
if (result?.Succeeded ?? false)
{
var authFeatures = new AuthenticationFeatures(result);
context.Features.Set<IHttpAuthenticationFeature>(authFeatures);
context.Features.Set<IAuthenticateResultFeature>(authFeatures);
}
}
await _next(context);
}