認證authentication,基於聲明式認證
基於HttpContext的認證的擴展,SignIn(成功會頒發一個加密的憑證)、SignOut、Authenticate (驗證signin頒發證書,返回authenticationResult,表明用戶身份)、Challenge(返回一個需要標識身份提示用戶登錄,通常返回401)、 Forbid (通用返回403)、 GetTocken,調用AuthenticationService同名方法執行
在aspnetcore的http下authentication.abstrations與authentication.core對關鍵抽象進行描述,Security下則是Authentication則是具體的實現
服務注入 AddAuthentication(),可以直接指定defaultscheme,可以使用委托方法指定AuthenticationOption下面的defaultscheme,authenticatescheme,signscheme,signoutscheme,defaultchallenge
指定相應的hander。注入最重要AuthenticationService、 AuthenticationHandlerProvider、AuthenticationSchemeProvider三個重要對象
services.AddAuthenticationCore(); services.AddDataProtection(); services.AddWebEncoders(); services.TryAddSingleton<ISystemClock, SystemClock>(); return new AuthenticationBuilder(services); public static AuthenticationBuilder AddAuthentication(this IServiceCollection services, string defaultScheme) => services.AddAuthentication(o => o.DefaultScheme = defaultScheme); public static AuthenticationBuilder AddAuthentication(this IServiceCollection services, Action<AuthenticationOptions> configureOptions) services.Configure(configureOptions);
通過AddAuthentication返回的AuthenticationBuilder,通過AddJwtBearer(或者AddCookie)來指定Scheme類型和需要驗證的參數
context.Services.AddAuthentication("Bearer") .AddIdentityServerAuthentication(options => { options.Authority = configuration["AuthServer:Authority"]; options.ApiName = configuration["AuthServer:ApiName"]; options.RequireHttpsMetadata = false; });
public static AuthenticationBuilder AddIdentityServerAuthentication(this AuthenticationBuilder builder, string authenticationScheme, Action<IdentityServerAuthenticationOptions> configureOptions) { builder.AddJwtBearer(authenticationScheme + IdentityServerAuthenticationDefaults.JwtAuthenticationScheme, configureOptions: null); builder.AddOAuth2Introspection(authenticationScheme + IdentityServerAuthenticationDefaults.IntrospectionAuthenticationScheme, configureOptions: null); builder.Services.AddSingleton<IConfigureOptions<JwtBearerOptions>>(services => { var monitor = services.GetRequiredService<IOptionsMonitor<IdentityServerAuthenticationOptions>>(); return new ConfigureInternalOptions(monitor.Get(authenticationScheme), authenticationScheme); }); builder.Services.AddSingleton<IConfigureOptions<OAuth2IntrospectionOptions>>(services => { var monitor = services.GetRequiredService<IOptionsMonitor<IdentityServerAuthenticationOptions>>(); return new ConfigureInternalOptions(monitor.Get(authenticationScheme), authenticationScheme); }); return builder.AddScheme<IdentityServerAuthenticationOptions, IdentityServerAuthenticationHandler>(authenticationScheme, configureOptions); }
在Startup類中的Configure方法通過添加UseAuthentication注冊認證中間件(AuthenticationMiddleware),在認證過程中,通過AuthenticationSchemeProvider獲取正確的Scheme,在AuthenticationService中通過Scheme和AuthenticationHandlerProvider獲取正確的AuthenticationHandler,最后通過對應的AuthenticationHandler的AuthenticateAsync方法進行認證流程。
GetRequestHandlerSchemesAsync(多個AuthenticationScheme)=》GetDefaultAuthenticateSchemeAsync(一個AuthenticationScheme、執行handle下的AuthenticateAsync方法,返回AuthenticateResult給httpcontext.user賦值)
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; } } await _next(context); }
1、AuthenticationOption
scheme:有cookie, bearer, oauth, openid等等,保存着IList<AuthenticationSchemeBuilder> schemes,SchemeMap
DefaultScheme、DefaultAuthenticateScheme、DefaultSignInScheme、DefaultSignOutScheme、DefaultChallengeScheme、DefaultForbidScheme??
什么時候賦值??schememap對應是哪個AuthenticationSchemeBuilder,即使用哪個IAuthenticationHandle(方法有InitializeAsync、AuthenticateAsync、ChallengeAsync、ForbidAsync,Signin SignOut方法單獨出來)處理它的通用方法是AddScheme(),即增加到IList<AuthenticationSchemeBuilder>,每一個schemeName映射 Dictionary< schemeName , AuthenticationSchemeBuilder> schememap
它定義一個抽象方法HandleAuthenticateAsync,並使用HandleAuthenticateOnceAsync方法來保證其在每次認證只執行一次。而HandleAuthenticateAsync是認證的核心,交給具體的認證Handler負責實現。而對於 ChallengeAsync, ForbidAsync 等方法也提供了默認的實現。
而對於HandleAuthenticateAsync的實現,大致的邏輯就是從請求中獲取上面發放的身份令牌,然后解析成AuthenticationTicket,並經過一系列的驗證,最終返回ClaimsPrincipal對象。
jwtScheme=Bearer+IdentityServerAuthenticationJwt =》使用是JwtBearerHandler
introspectionScheme=Bearer+IdentityServerAuthenticationIntrospection 》對reference token處理
在HttpContext.Item存儲idsrv4:tokenvalidation:token
RemoteAuthenticationHandler 便是所有遠程認證的抽象基類了,它繼承自AuthenticationHandler,並實現了IAuthenticationRequestHandler接口:
而RemoteAuthenticationHandler中核心的認證邏輯便是 HandleRequestAsync 方法,它主要包含2個步驟:
首先執行一個抽象方法HandleRemoteAuthenticateAsync,由具體的Handler來實現,該方法返回的HandleRequestResult對象包含驗證的結果(跳過,失敗,成功等),在成功時會包含一個ticket對象若上一步驗證成功,則根據返回的ticket,獲取到ClaimsPrincipal對象,並調用其它認證Handler的Context.SignInAsync方法。
遠程Hander會在用戶未登錄時,指引用戶跳轉到認證服務器,登錄成功后,解析認證服務器傳回的憑證,最終依賴於本地Handler來保存身份令牌。當用戶再次訪問則無需經過遠程Handler,直接交給本地Handler來處理。
public interface IAuthenticationRequestHandler : IAuthenticationHandler
{
/// <summary>
/// Returns true if request processing should stop.
/// </summary>
/// <returns><see langword="true" /> if request processing should stop.</returns>
Task<bool> HandleRequestAsync(); } public interface IAuthenticationSignInHandler : IAuthenticationSignOutHandler { Task SignInAsync(ClaimsPrincipal user, AuthenticationProperties properties); } public interface IAuthenticationSignOutHandler : IAuthenticationHandler { Task SignOutAsync(AuthenticationProperties properties); }
