ASP.NET Core Authentication認(rèn)證實(shí)現(xiàn)方法
追本溯源,從使用開(kāi)始
首先看一下我們通常是如何使用微軟自帶的認(rèn)證,一般在Startup里面配置我們所需的依賴(lài)認(rèn)證服務(wù),這里通過(guò)JWT的認(rèn)證方式講解
public void ConfigureServices(IServiceCollection services) { services.AddAuthentication(authOpt => { authOpt.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme; authOpt.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme; }) .AddJwtBearer(o => { o.TokenValidationParameters = new TokenValidationParameters { //配置自己所要驗(yàn)證的參數(shù) }; }); }
我們來(lái)看一下源碼AddAuthentication主要做了什么
public static class AuthenticationServiceCollectionExtensions { public static AuthenticationBuilder AddAuthentication( this IServiceCollection services, Action<AuthenticationOptions> configureOptions) { if (services == null) throw new ArgumentNullException(nameof (services)); if (configureOptions == null) throw new ArgumentNullException(nameof (configureOptions)); AuthenticationBuilder authenticationBuilder = services.AddAuthentication(); services.Configure<AuthenticationOptions>(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<ISystemClock, SystemClock>(); return new AuthenticationBuilder(services); } public static AuthenticationBuilder AddAuthentication( this IServiceCollection services, string defaultScheme) { return services.AddAuthentication((Action<AuthenticationOptions>) (o => o.DefaultScheme = defaultScheme)); } ..... }
ConfigureServices方法基本都是服務(wù)的注冊(cè),基于微軟的風(fēng)格,這里的AddAuthenticationCore肯定是我們的認(rèn)證服務(wù)注冊(cè)方法,來(lái)看一下
public static class AuthenticationCoreServiceCollectionExtensions { /// <summary> /// Add core authentication services needed for <see cref="T:Microsoft.AspNetCore.Authentication.IAuthenticationService" />. /// </summary> public static IServiceCollection AddAuthenticationCore( this IServiceCollection services) { if (services == null) throw new ArgumentNullException(nameof (services)); services.TryAddScoped<IAuthenticationService, AuthenticationService>(); services.TryAddSingleton<IClaimsTransformation, NoopClaimsTransformation>(); services.TryAddScoped<IAuthenticationHandlerProvider, AuthenticationHandlerProvider>(); services.TryAddSingleton<IAuthenticationSchemeProvider, AuthenticationSchemeProvider>(); return services; } /// <summary> /// Add core authentication services needed for <see cref="T:Microsoft.AspNetCore.Authentication.IAuthenticationService" />. /// </summary> public static IServiceCollection AddAuthenticationCore( this IServiceCollection services, Action<AuthenticationOptions> configureOptions) { if (services == null) throw new ArgumentNullException(nameof (services)); if (configureOptions == null) throw new ArgumentNullException(nameof (configureOptions)); services.AddAuthenticationCore(); services.Configure<AuthenticationOptions>(configureOptions); return services; } }
我們看到這里主要注冊(cè)了AuthenticationService, AuthenticationHandlerProvider, AuthenticationSchemeProvider這三個(gè)對(duì)象,如文章開(kāi)頭所說(shuō),追本溯源,從使用開(kāi)始,我們先看一下這三個(gè)對(duì)象是如何在認(rèn)證體系中使用的,且是如何發(fā)揮作用的。
從使用開(kāi)始
看一下我們的認(rèn)證管道構(gòu)建
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<AuthenticationMiddleware>(); } }
這里使用了約定的注冊(cè)方式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>((IAuthenticationFeature) new AuthenticationFeature() { OriginalPath = context.Request.Path, OriginalPathBase = context.Request.PathBase }); IAuthenticationHandlerProvider handlers = context.RequestServices.GetRequiredService<IAuthenticationHandlerProvider>(); 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); //實(shí)際的認(rèn)證業(yè)務(wù) if (authenticateResult?.Principal != null) context.User = authenticateResult.Principal; } await this._next(context); } }
在繼續(xù)往下之前,我們先看一下這個(gè)認(rèn)證中間件的作用結(jié)果,當(dāng)認(rèn)證通過(guò)時(shí),在HttpContext的User屬性(ClaimPrincipal)賦予身份標(biāo)識(shí),所以在后續(xù)的請(qǐng)求管道中都是基于認(rèn)證結(jié)果中的身份標(biāo)識(shí)做鑒權(quán),這個(gè)我們會(huì)在后面的實(shí)際操作中會(huì)提到。
言歸正傳,在這里引出了我們的兩個(gè)對(duì)象AuthenticationHandlerProvider,AuthenticationSchemeProvider。
重要對(duì)象講解
IAuthenticationSchemeProvider
從名字來(lái)看,IAuthenticationSchemeProvider的作用應(yīng)該是提供Scheme的,這也是Provider在微軟的風(fēng)格里面起的作用(類(lèi)似于工廠模式)。
這個(gè)Scheme是什么呢?很明顯,在Framework時(shí)代,也是有基于不同Scheme驗(yàn)證的,比如Bearer,Cookie,在Aspnet Core中定義不同的Scheme代表著不同的認(rèn)證處理方式,具體體現(xiàn)是在每個(gè)Scheme中包含對(duì)應(yīng)的IAuthenticationHandler類(lèi)型的Handler,由它來(lái)完成跟自身Scheme相關(guān)的認(rèn)證處理。如果沒(méi)有定義會(huì)怎么樣?仔細(xì)看上面這塊源碼,只有當(dāng)AuthenticationScheme不為空時(shí)才會(huì)做認(rèn)證,否則一旦在Controller打上鑒權(quán)標(biāo)簽[Authorize],將會(huì)直接返回401,所以我們必須指定自己的Scheme。
那么我們?cè)谀睦镏付ㄎ覀兊腟cheme類(lèi)似呢?我們先返回到ConfigureService的AddJwtBearer,使用過(guò)的朋友們肯定知道,這里獲取的Scheme是我們?cè)贑onfigureService通過(guò)Addxxx scheme指定的Scheme類(lèi)型。這里我們是使用JWT的
在這里指定了TOptions 為JwtBearerOptions,而THandler為JwtBearerHandler。
public virtual AuthenticationBuilder AddScheme<TOptions, THandler>( string authenticationScheme, string displayName, Action<TOptions> configureOptions) where TOptions : AuthenticationSchemeOptions, new() where THandler : AuthenticationHandler<TOptions> { return this.AddSchemeHelper<TOptions, THandler>(authenticationScheme, displayName, configureOptions); } private AuthenticationBuilder AddSchemeHelper<TOptions, THandler>( string authenticationScheme, string displayName, Action<TOptions> configureOptions) where TOptions : class, new() where THandler : class, IAuthenticationHandler { this.Services.Configure<AuthenticationOptions>((Action<AuthenticationOptions>) (o => o.AddScheme(authenticationScheme, (Action<AuthenticationSchemeBuilder>) (scheme => { scheme.HandlerType = typeof (THandler); scheme.DisplayName = displayName; })))); if (configureOptions != null) this.Services.Configure<TOptions>(authenticationScheme, configureOptions); this.Services.AddTransient<THandler>(); return this; }
注意這里TOptions 是需要繼承AuthenticationSchemeOptions的,在這里是JwtBearerOptions,而THandler是AuthenticationHandler<TOptions>類(lèi)型的Handler,在這里是JwtBearerHandler。
我們回到Scheme的分析繼續(xù)往下,首先看一下AuthenticationScheme的定義
public class AuthenticationScheme { /// <summary>Constructor.</summary> 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; } /// <summary>The name of the authentication scheme.</summary> public string Name { get; } /// <summary> /// The display name for the scheme. Null is valid and used for non user facing schemes. /// </summary> public string DisplayName { get; } /// <summary> /// The <see cref="T:Microsoft.AspNetCore.Authentication.IAuthenticationHandler" /> type that handles this scheme. /// </summary> public Type HandlerType { get; } }
在這里可以看到,如果要使用Aspnet Core自身的認(rèn)證體系,需先注冊(cè)Scheme,并且該Scheme必須指定一個(gè)類(lèi)型為IAuthenticationHandler的Handler,否則會(huì)拋出異常。(這個(gè)其實(shí)在AddxxxScheme的時(shí)候已經(jīng)指定了AuthenticationHandler)
我們?cè)倏匆幌翴AuthenticationSchemeProvider的GetRequestHandlerSchemesAsync方法做了什么
public virtual Task<IEnumerable<AuthenticationScheme>> GetRequestHandlerSchemesAsync() { return Task.FromResult<IEnumerable<AuthenticationScheme>>((IEnumerable<AuthenticationScheme>) this._requestHandlers); }
這東西返回了_requestHandlers,這是什么?看代碼
public class AuthenticationSchemeProvider : IAuthenticationSchemeProvider { private readonly object _lock = new object(); private readonly AuthenticationOptions _options; private readonly IDictionary<string, AuthenticationScheme> _schemes; private readonly List<AuthenticationScheme> _requestHandlers; /// <summary> /// Creates an instance of <see cref="T:Microsoft.AspNetCore.Authentication.AuthenticationSchemeProvider" /> /// using the specified <paramref name="options" />, /// </summary> public AuthenticationSchemeProvider(IOptions<AuthenticationOptions> options) : this(options, (IDictionary<string, AuthenticationScheme>) new Dictionary<string, AuthenticationScheme>((IEqualityComparer<string>) StringComparer.Ordinal)) { } /// <summary> /// Creates an instance of <see cref="T:Microsoft.AspNetCore.Authentication.AuthenticationSchemeProvider" /> /// using the specified <paramref name="options" /> and <paramref name="schemes" />. /// </summary> protected AuthenticationSchemeProvider( IOptions<AuthenticationOptions> options, IDictionary<string, AuthenticationScheme> schemes) { this._options = options.Value; IDictionary<string, AuthenticationScheme> dictionary = schemes; if (dictionary == null) throw new ArgumentNullException(nameof (schemes)); this._schemes = dictionary; this._requestHandlers = new List<AuthenticationScheme>(); foreach (AuthenticationSchemeBuilder scheme in this._options.Schemes) this.AddScheme(scheme.Build()); } public virtual void AddScheme(AuthenticationScheme scheme) { if (this._schemes.ContainsKey(scheme.Name)) throw new InvalidOperationException("Scheme already exists: " + scheme.Name); lock (this._lock) { if (this._schemes.ContainsKey(scheme.Name)) throw new InvalidOperationException("Scheme already exists: " + scheme.Name); if (typeof (IAuthenticationRequestHandler).IsAssignableFrom(scheme.HandlerType)) this._requestHandlers.Add(scheme); this._schemes[scheme.Name] = scheme; } } ..... }
這東西就是把我們?cè)谡J(rèn)證注冊(cè)服務(wù)中指定的scheme,通過(guò)解析出的AuthenticationSchemeProvider 的構(gòu)造函數(shù)加載來(lái)的,進(jìn)而返回一系列的List<AuthenticationScheme>,OK拿到這些scheme之后有什么用呢?這里引出了我們的第二個(gè)對(duì)象AuthenticationHandlerProvider,下面我們來(lái)了解一下?! ?/p>
IAuthenticationHandlerProvider
我們看到,AuthenticationMiddleware中用到了IAuthenticationHandlerProvider的GetHandlerAsync方法,那我們先看一下這個(gè)方法的作用
public class AuthenticationHandlerProvider : IAuthenticationHandlerProvider { private Dictionary<string, IAuthenticationHandler> _handlerMap = new Dictionary<string, IAuthenticationHandler>((IEqualityComparer<string>) StringComparer.Ordinal); /// <summary>Constructor.</summary> public AuthenticationHandlerProvider(IAuthenticationSchemeProvider schemes) { this.Schemes = schemes; } /// <summary> /// The <see cref="T:Microsoft.AspNetCore.Authentication.IAuthenticationHandlerProvider" />. /// </summary> public IAuthenticationSchemeProvider Schemes { get; } /// <summary>Returns the handler instance that will be used.</summary> public async Task<IAuthenticationHandler> GetHandlerAsync( HttpContext context, string authenticationScheme) { if (this._handlerMap.ContainsKey(authenticationScheme)) return this._handlerMap[authenticationScheme]; AuthenticationScheme schemeAsync = await this.Schemes.GetSchemeAsync(authenticationScheme); if (schemeAsync == null) return (IAuthenticationHandler) null; IAuthenticationHandler handler = (context.RequestServices.GetService(schemeAsync.HandlerType) ?? ActivatorUtilities.CreateInstance(context.RequestServices, schemeAsync.HandlerType)) as IAuthenticationHandler; if (handler != null) { await handler.InitializeAsync(schemeAsync, context); this._handlerMap[authenticationScheme] = handler; } return handler; } }
在創(chuàng)建Handler的時(shí)候,是先從AuthenticationScheme中獲取,如果不存在則通過(guò)ActivatorUtilities創(chuàng)建。 獲取到Handle后,將會(huì)放在_handlerMap字典里面,當(dāng)下次獲取Handler的時(shí)候,將直接從緩存中獲取。
IAuthenticationService
這個(gè)對(duì)象是在AuthenticationMiddleware中最后才用到的,而且是基于HttpContext的擴(kuò)展被調(diào)用
public static class AuthenticationHttpContextExtensions { public static Task<AuthenticateResult> AuthenticateAsync(this HttpContext context, string scheme) => context.RequestServices.GetRequiredService<IAuthenticationService>().AuthenticateAsync(context, scheme); .... }
這里主要調(diào)用了IAuthenticationService的AuthenticateAsync方法,看一下這個(gè)方法做了什么
public class AuthenticationService : IAuthenticationService { public IAuthenticationSchemeProvider Schemes { get; } public IAuthenticationHandlerProvider Handlers { get; } public IClaimsTransformation Transform { get; } public virtual async Task<AuthenticateResult> AuthenticateAsync(HttpContext context, string scheme) { if (scheme == null) { var scheme = (await this.Schemes.GetDefaultAuthenticateSchemeAsync())?.Name; if (scheme == null) throw new InvalidOperationException($"No authenticationScheme was specified, and there was no DefaultAuthenticateScheme found."); } var handler = await Handlers.GetHandlerAsync(context, scheme); if(handler == null) throw await this.CreateMissingHandlerException(scheme); AuthenticateResult result = await handler.AuthenticateAsync(); if (result != null && result.Succeeded) return AuthenticateResult.Success(new AuthenticationTicket(await Transform.TransformAsync(result.Principal), result.Properties, result.Ticket.AuthenticationScheme)); return result; } }
這里其實(shí)就是我們?cè)谇懊嬷v的根據(jù)Scheme獲取對(duì)應(yīng)的AuthenticationHandler,然后調(diào)用AuthenticateAsync()方法,這個(gè)方法調(diào)用了核心方法HandleAuthenticateOnceAsync,然后再調(diào)用HandleAuthenticateAsync()這個(gè)核心的認(rèn)證方法。
從上圖看到這個(gè)HandleAuthenticateAsync是個(gè)抽象方法,我們的子類(lèi)都需要實(shí)現(xiàn)這個(gè)方法的動(dòng)作,基于本文的例子,我們看一下JwtBearerHandler的一個(gè)實(shí)際認(rèn)證。
public class JwtBearerHandler : AuthenticationHandler<JwtBearerOptions> { protected override async Task<AuthenticateResult> HandleAuthenticateAsync() { JwtBearerHandler jwtBearerHandler = this; string token = (string) null; object obj; AuthenticationFailedContext authenticationFailedContext; int num; try { MessageReceivedContext messageReceivedContext = new MessageReceivedContext(jwtBearerHandler.Context, jwtBearerHandler.Scheme, jwtBearerHandler.Options); await jwtBearerHandler.Events.MessageReceived(messageReceivedContext); if (messageReceivedContext.Result != null) return messageReceivedContext.Result; token = messageReceivedContext.Token; if (string.IsNullOrEmpty(token)) { string header = (string) jwtBearerHandler.Request.Headers["Authorization"]; if (string.IsNullOrEmpty(header)) return AuthenticateResult.NoResult(); if (header.StartsWith("Bearer ", StringComparison.OrdinalIgnoreCase)) token = header.Substring("Bearer ".Length).Trim(); if (string.IsNullOrEmpty(token)) return AuthenticateResult.NoResult(); } if (jwtBearerHandler._configuration == null && jwtBearerHandler.Options.ConfigurationManager != null) { OpenIdConnectConfiguration configurationAsync = await jwtBearerHandler.Options.ConfigurationManager.GetConfigurationAsync(jwtBearerHandler.Context.RequestAborted); jwtBearerHandler._configuration = configurationAsync; } TokenValidationParameters validationParameters1 = jwtBearerHandler.Options.TokenValidationParameters.Clone(); if (jwtBearerHandler._configuration != null) { string[] strArray = new string[1] { jwtBearerHandler._configuration.Issuer }; TokenValidationParameters validationParameters2 = validationParameters1; IEnumerable<string> validIssuers = validationParameters1.get_ValidIssuers(); object obj1 = (validIssuers != null ? (object) validIssuers.Concat<string>((IEnumerable<string>) strArray) : (object) null) ?? (object) strArray; validationParameters2.set_ValidIssuers((IEnumerable<string>) obj1); TokenValidationParameters validationParameters3 = validationParameters1; IEnumerable<SecurityKey> issuerSigningKeys = validationParameters1.get_IssuerSigningKeys(); IEnumerable<SecurityKey> securityKeys = (issuerSigningKeys != null ? issuerSigningKeys.Concat<SecurityKey>((IEnumerable<SecurityKey>) jwtBearerHandler._configuration.get_SigningKeys()) : (IEnumerable<SecurityKey>) null) ?? (IEnumerable<SecurityKey>) jwtBearerHandler._configuration.get_SigningKeys(); validationParameters3.set_IssuerSigningKeys(securityKeys); } List<Exception> exceptionList = (List<Exception>) null; foreach (ISecurityTokenValidator securityTokenValidator in (IEnumerable<ISecurityTokenValidator>) jwtBearerHandler.Options.SecurityTokenValidators) { if (securityTokenValidator.CanReadToken(token)) { SecurityToken securityToken; ClaimsPrincipal claimsPrincipal; try { claimsPrincipal = securityTokenValidator.ValidateToken(token, validationParameters1, ref securityToken); } catch (Exception ex) { jwtBearerHandler.Logger.TokenValidationFailed(ex); if (jwtBearerHandler.Options.RefreshOnIssuerKeyNotFound && jwtBearerHandler.Options.ConfigurationManager != null && ex is SecurityTokenSignatureKeyNotFoundException) jwtBearerHandler.Options.ConfigurationManager.RequestRefresh(); if (exceptionList == null) exceptionList = new List<Exception>(1); exceptionList.Add(ex); continue; } jwtBearerHandler.Logger.TokenValidationSucceeded(); TokenValidatedContext validatedContext = new TokenValidatedContext(jwtBearerHandler.Context, jwtBearerHandler.Scheme, jwtBearerHandler.Options); validatedContext.Principal = claimsPrincipal; validatedContext.SecurityToken = securityToken; TokenValidatedContext tokenValidatedContext = validatedContext; await jwtBearerHandler.Events.TokenValidated(tokenValidatedContext); if (tokenValidatedContext.Result != null) return tokenValidatedContext.Result; if (jwtBearerHandler.Options.SaveToken) tokenValidatedContext.Properties.StoreTokens((IEnumerable<AuthenticationToken>) new AuthenticationToken[1] { new AuthenticationToken() { Name = "access_token", Value = token } }); tokenValidatedContext.Success(); return tokenValidatedContext.Result; } } if (exceptionList == null) return AuthenticateResult.Fail("No SecurityTokenValidator available for token: " + token ?? "[null]"); authenticationFailedContext = new AuthenticationFailedContext(jwtBearerHandler.Context, jwtBearerHandler.Scheme, jwtBearerHandler.Options) { Exception = exceptionList.Count == 1 ? exceptionList[0] : (Exception) new AggregateException((IEnumerable<Exception>) exceptionList) }; await jwtBearerHandler.Events.AuthenticationFailed(authenticationFailedContext); return authenticationFailedContext.Result == null ? AuthenticateResult.Fail(authenticationFailedContext.Exception) : authenticationFailedContext.Result; } catch (Exception ex) { obj = (object) ex; num = 1; } if (num == 1) { Exception ex = (Exception) obj; jwtBearerHandler.Logger.ErrorProcessingMessage(ex); authenticationFailedContext = new AuthenticationFailedContext(jwtBearerHandler.Context, jwtBearerHandler.Scheme, jwtBearerHandler.Options) { Exception = ex }; await jwtBearerHandler.Events.AuthenticationFailed(authenticationFailedContext); if (authenticationFailedContext.Result != null) return authenticationFailedContext.Result; Exception source = obj as Exception; if (source == null) throw obj; ExceptionDispatchInfo.Capture(source).Throw(); authenticationFailedContext = (AuthenticationFailedContext) null; } obj = (object) null; token = (string) null; AuthenticateResult authenticateResult; return authenticateResult; } }
這個(gè)方法有點(diǎn)長(zhǎng),主要是從Request.Headers里面獲取Authorization的Bearer出來(lái)解析,再在AddJwtBearer中傳入的委托參數(shù)JwtBearerOptions的TokenValidationParameters屬性作為依據(jù)進(jìn)行對(duì)比來(lái)進(jìn)行認(rèn)證是否通過(guò)與否。
總結(jié)
本文對(duì) ASP.NET Core 的認(rèn)證流程做了一個(gè)源碼分析流程介紹,由于是源碼分析篇,所以可能會(huì)比較枯燥和苦澀難懂。在后面的真正使用過(guò)程中,然后再結(jié)合本篇的一個(gè)總結(jié)流程,相信大家會(huì)逐漸開(kāi)朗。
- 在Startup類(lèi)中的ConfigureServices方法通過(guò)添加AddAuthentication注冊(cè)我們最主要的三個(gè)對(duì)象AuthenticationService, AuthenticationHandlerProvider, AuthenticationSchemeProvider
- 通過(guò)AddAuthentication返回的AuthenticationBuilder 通過(guò)AddJwtBearer(或者AddCookie)來(lái)指定Scheme類(lèi)型和需要驗(yàn)證的參數(shù)
- 在Startup類(lèi)中的Configure方法通過(guò)添加UseAuthentication注冊(cè)認(rèn)證中間件
- 在認(rèn)證過(guò)程中,通過(guò)AuthenticationSchemeProvider獲取正確的Scheme,在AuthenticationService中通過(guò)Scheme和AuthenticationHandlerProvider獲取正確的AuthenticationHandler,最后通過(guò)對(duì)應(yīng)的AuthenticationHandler的AuthenticateAsync方法進(jìn)行認(rèn)證流程
到此這篇關(guān)于ASP.NET Core Authentication認(rèn)證實(shí)現(xiàn)方法的文章就介紹到這了,更多相關(guān)ASP.NET Core Authentication認(rèn)證內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- ASP.NET Core3.1 Ocelot認(rèn)證的實(shí)現(xiàn)
- ASP.NET Core使用JWT認(rèn)證授權(quán)的方法
- 深入解讀ASP.NET Core身份認(rèn)證過(guò)程實(shí)現(xiàn)
- ASP.NET Core 實(shí)現(xiàn)基本認(rèn)證的示例代碼
- ASP.NET Core學(xué)習(xí)之使用JWT認(rèn)證授權(quán)詳解
- Asp.net Core中實(shí)現(xiàn)自定義身份認(rèn)證的示例代碼
- 淺談ASP.NET Core 中jwt授權(quán)認(rèn)證的流程原理
- ASP.Net Core3.0中使用JWT認(rèn)證的實(shí)現(xiàn)
- Asp.Net Core基于JWT認(rèn)證的數(shù)據(jù)接口網(wǎng)關(guān)實(shí)例代碼
- ASP.NET學(xué)習(xí)CORE中使用Cookie身份認(rèn)證方法
- Asp.Net Core添加請(qǐng)求頭自定義認(rèn)證的示例
相關(guān)文章
微軟發(fā)布的Data Access Application Block的使用代碼
微軟發(fā)布的Data Access Application Block的使用代碼...2007-04-04為HttpClient添加默認(rèn)請(qǐng)求報(bào)頭的四種解決方案
這篇文章主要給大家介紹了關(guān)于為HttpClient添加默認(rèn)請(qǐng)求報(bào)頭的四種解決方案,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家學(xué)習(xí)或者使用HttpClient具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2019-09-09ASP.NET?MVC增加一條記錄同時(shí)添加N條集合屬性所對(duì)應(yīng)的個(gè)體
這篇文章介紹了ASP.NET?MVC增加一條記錄同時(shí)添加N條集合屬性所對(duì)應(yīng)個(gè)體的方法,文中通過(guò)示例代碼介紹的非常詳細(xì)。對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2022-08-08MVC使用Log4Net進(jìn)行錯(cuò)誤日志記錄學(xué)習(xí)筆記4
這篇文章主要為大家詳細(xì)介紹了MVC使用Log4Net進(jìn)行錯(cuò)誤日志記錄,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2016-09-09.NET?6開(kāi)發(fā)TodoList應(yīng)用之實(shí)現(xiàn)Repository模式
這篇文章主要介紹了如何實(shí)現(xiàn)一個(gè)可重用的Repository模塊。文中的示例代碼講解詳細(xì),對(duì)我們學(xué)習(xí)或工作有一定的幫助,感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下2021-12-12.NET Core使用FluentEmail發(fā)送郵件的示例代碼
這篇文章主要介紹了.NET Core使用FluentEmail發(fā)送郵件的示例代碼,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-10-10