您现在的位置是:网站首页> 编程资料编程资料

ASP.NET Core Authentication认证实现方法_实用技巧_

2023-05-24 295人已围观

简介 ASP.NET Core Authentication认证实现方法_实用技巧_

追本溯源,从使用开始  

  首先看一下我们通常是如何使用微软自带的认证,一般在Startup里面配置我们所需的依赖认证服务,这里通过JWT的认证方式讲解

 public void ConfigureServices(IServiceCollection services) { services.AddAuthentication(authOpt => { authOpt.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme; authOpt.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme; }) .AddJwtBearer(o => { o.TokenValidationParameters = new TokenValidationParameters { //配置自己所要验证的参数 }; }); }

  我们来看一下源码AddAuthentication主要做了什么

 public static class AuthenticationServiceCollectionExtensions { public static AuthenticationBuilder AddAuthentication( this IServiceCollection services, Action configureOptions) { if (services == null) throw new ArgumentNullException(nameof (services)); if (configureOptions == null) throw new ArgumentNullException(nameof (configureOptions)); AuthenticationBuilder authenticationBuilder = services.AddAuthentication(); services.Configure(configureOptions); return authenticationBuilder; } public static AuthenticationBuilder AddAuthentication( this IServiceCollection services) { if (services == null) throw new ArgumentNullException(nameof (services)); services.AddAuthenticationCore(); services.AddDataProtection(); services.AddWebEncoders(); services.TryAddSingleton(); return new AuthenticationBuilder(services); } public static AuthenticationBuilder AddAuthentication( this IServiceCollection services, string defaultScheme) { return services.AddAuthentication((Action) (o => o.DefaultScheme = defaultScheme)); } ..... }

  ConfigureServices方法基本都是服务的注册,基于微软的风格,这里的AddAuthenticationCore肯定是我们的认证服务注册方法,来看一下

 public static class AuthenticationCoreServiceCollectionExtensions { ///  /// Add core authentication services needed for . ///  public static IServiceCollection AddAuthenticationCore( this IServiceCollection services) { if (services == null) throw new ArgumentNullException(nameof (services)); services.TryAddScoped(); services.TryAddSingleton(); services.TryAddScoped(); services.TryAddSingleton(); return services; } ///  /// Add core authentication services needed for . ///  public static IServiceCollection AddAuthenticationCore( this IServiceCollection services, Action configureOptions) { if (services == null) throw new ArgumentNullException(nameof (services)); if (configureOptions == null) throw new ArgumentNullException(nameof (configureOptions)); services.AddAuthenticationCore(); services.Configure(configureOptions); return services; } }

  我们看到这里主要注册了AuthenticationService, AuthenticationHandlerProvider, AuthenticationSchemeProvider这三个对象,如文章开头所说,追本溯源,从使用开始,我们先看一下这三个对象是如何在认证体系中使用的,且是如何发挥作用的。

  从使用开始

  看一下我们的认证管道构建

 public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory) { ... app.UseAuthentication(); ... }  public static class AuthAppBuilderExtensions { public static IApplicationBuilder UseAuthentication( this IApplicationBuilder app) { if (app == null) throw new ArgumentNullException(nameof (app)); return app.UseMiddleware(); } }

  这里使用了约定的注册方式UseMiddleware,并且指定使用中间件AuthenticationMiddleware  

 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)); this._next = next; this.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 }); IAuthenticationHandlerProvider handlers = context.RequestServices.GetRequiredService(); foreach (AuthenticationScheme authenticationScheme in await this.Schemes.GetRequestHandlerSchemesAsync()) { IAuthenticationRequestHandler handlerAsync = await handlers.GetHandlerAsync(context, authenticationScheme.Name) as IAuthenticationRequestHandler; bool flag = handlerAsync != null; if (flag) flag = await handlerAsync.HandleRequestAsync(); if (flag) return; } AuthenticationScheme authenticateSchemeAsync = await this.Schemes.GetDefaultAuthenticateSchemeAsync(); if (authenticateSchemeAsync != null) { AuthenticateResult authenticateResult = await context.AuthenticateAsync(authenticateSchemeAsync.Name);  //实际的认证业务 if (authenticateResult?.Principal != null) context.User = authenticateResult.Principal; } await this._next(context); } }

  在继续往下之前,我们先看一下这个认证中间件的作用结果,当认证通过时,在HttpContext的User属性(ClaimPrincipal)赋予身份标识,所以在后续的请求管道中都是基于认证结果中的身份标识做鉴权,这个我们会在后面的实际操作中会提到。

  言归正传,在这里引出了我们的两个对象AuthenticationHandlerProvider,AuthenticationSchemeProvider。

  重要对象讲解

  IAuthenticationSchemeProvider

  从名字来看,IAuthenticationSchemeProvider的作用应该是提供Scheme的,这也是Provider在微软的风格里面起的作用(类似于工厂模式)。

  这个Scheme是什么呢?很明显,在Framework时代,也是有基于不同Scheme验证的,比如Bearer,Cookie,在Aspnet Core中定义不同的Scheme代表着不同的认证处理方式,具体体现是在每个Scheme中包含对应的IAuthenticationHandler类型的Handler,由它来完成跟自身Scheme相关的认证处理。如果没有定义会怎么样?仔细看上面这块源码,只有当AuthenticationScheme不为空时才会做认证,否则一旦在Controller打上鉴权标签[Authorize],将会直接返回401,所以我们必须指定自己的Scheme。

  那么我们在哪里指定我们的Scheme类似呢?我们先返回到ConfigureService的AddJwtBearer,使用过的朋友们肯定知道,这里获取的Scheme是我们在ConfigureService通过Addxxx scheme指定的Scheme类型。这里我们是使用JWT的

  在这里指定了TOptions 为JwtBearerOptions,而THandler为JwtBearerHandler。

 public virtual AuthenticationBuilder AddScheme( string authenticationScheme, string displayName, Action configureOptions) where TOptions : AuthenticationSchemeOptions, new() where THandler : AuthenticationHandler { return this.AddSchemeHelper(authenticationScheme, displayName, configureOptions); } private AuthenticationBuilder AddSchemeHelper( string authenticationScheme, string displayName, Action configureOptions) where TOptions : class, new() where THandler : class, IAuthenticationHandler { this.Services.Configure((Action) (o => o.AddScheme(authenticationScheme, (Action) (scheme => { scheme.HandlerType = typeof (THandler); scheme.DisplayName = displayName; })))); if (configureOptions != null) this.Services.Configure(authenticationScheme, configureOptions); this.Services.AddTransient(); return this; }

  注意这里TOptions 是需要继承AuthenticationSchemeOptions的,在这里是JwtBearerOptions,而THandler是AuthenticationHandler类型的Handler,在这里是JwtBearerHandler。

  我们回到Scheme的分析继续往下,首先看一下AuthenticationScheme的定义  

 public class AuthenticationScheme { /// Constructor. public AuthenticationScheme(string name, string displayName, Type handlerType) { if (name == null) throw new ArgumentNullException(nameof (name)); if (handlerType == (Type) null) throw new ArgumentNullException(nameof (handlerType)); if (!typeof (IAuthenticationHandler).IsAssignableFrom(handlerType)) throw new ArgumentException("handlerType must implement IAuthenticationHandler."); this.Name = name; this.HandlerType = handlerType; this.DisplayName = displayName; } /// The name of the authentication scheme. public string Name { get; } ///  /// The display name for the scheme. Null is valid and used for non user facing schemes. ///  public string DisplayName { get; } ///  /// The  type that handles this scheme. ///  public Type HandlerType { get; } }

  在这里可以看到,如果要使用Aspnet Core自身的认证体系,需先注册Scheme,并且该Scheme必须指定一个类型为IAuthenticationHandler的Handler,否则会抛出异常。(这个其实在AddxxxScheme的时候已经指定了AuthenticationHandler)

  我们再看一下IAuthenticationSchemeProvider的GetRequestHandlerSchemesAsync方法做了什么

   public virtual Task> GetRequestHandlerSchemesAsync() { return Task.FromResult>((IEnumerable) this._requestHandlers); }

  这东西返回了_requestHandlers,这是什么?看代码

 public class AuthenticationSchemeProvider : IAuthenticationSchemeProvider { private readonly object _lock = new object(); private readonly AuthenticationOptions _options; private readonly IDictionary _schemes; private readonly List _requestHandlers; ///  /// Creates an instan
                
                

-六神源码网