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这个方法,想怎么认证就怎么写。