我对ASP.NET Core 2中的身份验证有疑问:对于 app.UseAuthentication()的调用究竟是什么?
这是一个基本的先决条件,以便我可以实现我的自定义身份验证逻辑吗?我已经看过UseAuthentication以及实际中间件AuthenticationMiddleware的实现,但说实话,我不明白实际做了什么以及为什么有必要。
换句话说:
如果我没有打电话给 UseAuthentication(),我仍然对AuthenticationMiddleware实际上在做什么感兴趣。所以,如果你知道如果你能为我解释,我会非常感激。
答案 0 :(得分:10)
如果您编写自定义中间件(就像您在示例中所做的那样),则无需调用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 */ })
}
答案 1 :(得分:0)
你需要打电话。
UseAuthentication()
记录为:
将AuthenticationMiddleware添加到指定的IApplicationBuilder,后者启用身份验证功能。
它基本上是这样做的:
IApplicationBuilder AddAuthentication(this IApplicationBuilder app) {
return app.UseMiddleware<AuthenticationMiddleware>();
}
...所以它只是为你节省了一些打字,可能还有一些额外的using
进口。
这会在流程中添加AuthenticationMiddleware
个实例。请求处理管道,此特定对象添加管道以进行身份验证。
答案 2 :(得分:0)
尽管这是一个老话题,但是由于我最近偶然发现了一个相同的问题,我认为在内部进行更多的了解可能会使其他人受益
简短的答案取决于您的服务类型和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类中。
在这些情况下,不需要致电UseAuthentication
答案 3 :(得分:0)
来自 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);
}