Microsoft.Authentication的使用方法在2.0中發生了比較大的變化,在1.1中認證配置是在Configure中完成。
public void ConfigureServices(IServiceCollection services)
{
services.AddAuthentication();
}
public void Configure(IApplicationBuilder app)
{
app.UseJwtBearerAuthentication(new JwtBearerOptions
{
Authority = Configuration["jwt:authority"],
Audience = Configuration["jwt:audience"],
Events = new JwtBearerEvents()
{
OnAuthenticationFailed = c =>
{
c.HandleResponse();
c.Response.StatusCode = 500;
c.Response.ContentType = "text/plain";
if (Environment.IsDevelopment())
{
return c.Response.WriteAsync(c.Exception.ToString());
}
return c.Response.WriteAsync("An error occurred processing your authentication.");
}
}
});
UseJwtBearerAuthentication其實是添加了一個中間件
public static IApplicationBuilder UseJwtBearerAuthentication(this IApplicationBuilder app, JwtBearerOptions options)
{
if (app == null)
{
throw new ArgumentNullException(nameof(app));
}
if (options == null)
{
throw new ArgumentNullException(nameof(options));
}
return app.UseMiddleware<JwtBearerMiddleware>(Options.Create(options));
}
而在2.0中,認證配置則是在ConfigureServices中完成,並且通過Scheme-Handler的形式來實現多種認證方案的策略式選擇。
public void ConfigureServices(IServiceCollection services)
{
services.AddJwtBearerAuthentication(o =>
{
o.Authority = Configuration["jwt:authority"];
o.Audience = Configuration["jwt:audience"];
o.Events = new JwtBearerEvents()
{
OnAuthenticationFailed = c =>
{
c.HandleResponse();
c.Response.StatusCode = 500;
c.Response.ContentType = "text/plain";
if (Environment.IsDevelopment())
{
return c.Response.WriteAsync(c.Exception.ToString());
}
return c.Response.WriteAsync("An error occurred processing your authentication.");
}
};
});
}
public void Configure(IApplicationBuilder app)
{
app.UseAuthentication();
}
public static IServiceCollection AddJwtBearerAuthentication(this IServiceCollection services, string authenticationScheme, Action<JwtBearerOptions> configureOptions)
{
return services.AddScheme<JwtBearerOptions, JwtBearerHandler>(authenticationScheme, configureOptions);
}
public static IApplicationBuilder UseAuthentication(this IApplicationBuilder app)
{
if (app == null)
{
throw new ArgumentNullException(nameof(app));
}
return app.UseMiddleware<AuthenticationMiddleware>();
}
namespace Microsoft.AspNetCore.Authentication
{
public class AuthenticationMiddleware
{
private readonly RequestDelegate _next;
public AuthenticationMiddleware(RequestDelegate next, IAuthenticationSchemeProvider schemes)
{
if (next == null)
{
throw new ArgumentNullException(nameof(next));
}
if (schemes == null)
{
throw new ArgumentNullException(nameof(schemes));
}
_next = next;
Schemes = schemes;
}
public IAuthenticationSchemeProvider Schemes { get; set; }
public async Task Invoke(HttpContext context)
{
context.Features.Set<IAuthenticationFeature>(new AuthenticationFeature
{
OriginalPath = context.Request.Path,
OriginalPathBase = context.Request.PathBase
});
// REVIEW: alternatively could depend on a routing middleware to do this
// 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;
}
}
await _next(context);
}
}
}
也就是說,1.1的時候我們使用不同的認證方案時,是使用不同的中間件來實現認證,而2.0則剛好反過來,官方實現了一個統一的認證中間件,在中間件里獲取對應的Scheme的Handler,然后調用Handler來完成認證過程。
在2.0中實現自己的認證方案非常方便——自己實現一個AuthenticationSchemeOptions和一個AuthenticationHandler,然后通過AddScheme注入並指定Scheme就可以了。
以官方JwtBearerAuthentication為例:
在ConfigureServices中調用AddJwtBearerAuthentication,其實是調用了AddScheme,authenticationScheme是JwtBearerDefaults.AuthenticationScheme。
JwtBearerOptions是繼承AuthenticationSchemeOptions的類,用來保存認證配置。
JwtBearerHandler繼承了AuthenticationHandler<JwtBearerOptions>,用於認證過程處理,需要什么依賴,直接從構造函數注入。關鍵在HandleAuthenticateAsync和HandleUnauthorizedAsync這兩個方法。
認證流程是這樣的:
1. ConfigureServices中調用AddScheme提供<AuthenticationSchemeOptions,AuthenticationHandler>並指定Scheme。
2. Configure中調用UseAuthentication。
3. 訪問一個帶有AuthorizeAttribute的Action。
4. AuthenticationMiddleware獲取默認Scheme(或者AuthorizeAttribute指定的Scheme)的AuthenticationHandler,調用到Handler的HandleAuthenticateAsync,根據返回結果來決定是調用HandleUnauthorizedAsync還是HandleForbiddenAsync。
我們自己實現認證方案主要就是要實現HandleAuthenticateAsync這個方法,想怎么認證就怎么寫。
