通過前面對AddCookie 或者 AddOpenIdConnect 等了解,其實里面都實現了一個AuthenticationHandler<TOptions>的認證處理,接下來我們來簡單自定義一個試試
首先我來實現下面這個方式,我添加了一個AddLIYOUMING()
services.AddAuthentication(options => { options.DefaultAuthenticateScheme = "LIYOUMINGScheme"; options.DefaultChallengeScheme = "LIYOUMINGScheme"; }) .AddLIYOUMING(o=> { });
擴展下AuthenticationBuilder就行了,看下擴展
/// <summary> /// 黎又銘自定義可擴展 /// </summary> public static class LIYOUMINGExtensions { public static AuthenticationBuilder AddLIYOUMING(this AuthenticationBuilder builder) { builder.AddLIYOUMING("LIYOUMINGScheme", o => { }); return builder; } public static AuthenticationBuilder AddLIYOUMING(this AuthenticationBuilder builder, Action<LIYOUMINGOptions> configureOptions) { builder.AddLIYOUMING("LIYOUMINGScheme", configureOptions); return builder; } public static AuthenticationBuilder AddLIYOUMING(this AuthenticationBuilder builder, string defaultScheme, Action<LIYOUMINGOptions> configureOptions) { builder.Services.TryAddEnumerable(ServiceDescriptor.Singleton<IPostConfigureOptions<LIYOUMINGOptions>, LIYOUMINGPostConfigureOptions>()); builder.AddScheme<LIYOUMINGOptions, LIYOUMINGHandler>(defaultScheme, "", configureOptions); return builder; } }
我定義了LIYOUMINGOptions參數類,但是我並沒有添加任何參數明白原理即可,需要繼承AuthenticationSchemeOptions,可以重寫驗證處理,為什么要繼承AuthenticationSchemeOptions,是因為AuthenticationHandler<TOptions>泛型限定
public abstract class AuthenticationHandler<TOptions> : IAuthenticationHandler where TOptions : AuthenticationSchemeOptions, new()
public class LIYOUMINGOptions : AuthenticationSchemeOptions { public override void Validate() { base.Validate(); } public override void Validate(string scheme) { base.Validate(scheme); } }
還需要處理下配置,需要去實現IPostConfigureOptions
builder.Services.TryAddEnumerable(ServiceDescriptor.Singleton<IPostConfigureOptions<LIYOUMINGOptions>, LIYOUMINGPostConfigureOptions>());
public class LIYOUMINGPostConfigureOptions :IPostConfigureOptions<LIYOUMINGOptions> { public void PostConfigure(string name, LIYOUMINGOptions options) { } }
現在我們需要一個Handler來繼承AuthenticationHandler<TOptions>,這里我定義了一個LIYOUMINGHandler,里面去重寫相關認證方法即可,這里關鍵是AddScheme中的具體處理如下,配置綁定配置,注冊Handler服務:
public virtual AuthenticationBuilder AddScheme<TOptions, THandler>(string authenticationScheme, string displayName, Action<TOptions> configureOptions) where TOptions : AuthenticationSchemeOptions, new() where THandler : AuthenticationHandler<TOptions> { Services.Configure<AuthenticationOptions>(o => { o.AddScheme(authenticationScheme, scheme => { scheme.HandlerType = typeof(THandler); scheme.DisplayName = displayName; }); }); if (configureOptions != null) { Services.Configure(authenticationScheme, configureOptions); } Services.AddTransient<THandler>(); return this; }
下面就來看下我自定義的Handler中的處理
public class LIYOUMINGHandler : AuthenticationHandler<LIYOUMINGOptions> { public LIYOUMINGHandler(IOptionsMonitor<LIYOUMINGOptions> options, ILoggerFactory logger, UrlEncoder encoder,IDataProtectionProvider dataProtection, ISystemClock clock) : base(options, logger, encoder, clock) { } /// <summary> /// 這里就是具體的認證處理了 /// </summary> /// <returns></returns> protected async override Task<AuthenticateResult> HandleAuthenticateAsync() { AuthenticationTicket ticket = new AuthenticationTicket(new System.Security.Claims.ClaimsPrincipal { }, "LIYOUMINGScheme"); AuthenticateResult result = AuthenticateResult.Success(ticket); return await Task.FromResult(result); } protected override Task HandleChallengeAsync(AuthenticationProperties properties) { return base.HandleChallengeAsync(properties); } protected override Task HandleForbiddenAsync(AuthenticationProperties properties) { return base.HandleForbiddenAsync(properties); } protected override Task InitializeEventsAsync() { return base.InitializeEventsAsync(); } }
有些東西就沒寫了,無論你是cookie認證,還是OpenId 或者其他的都是在這里來處理的,只是具體的處理細節不一樣,前面一篇文章說過HandleAuthenticateAsync 其實是抽象方法在父類中AuthenticateAsync()被調用,而AuthenticateAsync(),在IAuthenticationHandleProvider被調用,所以這里具體看業務了
AuthenticationTicket ticket = new AuthenticationTicket(new System.Security.Claims.ClaimsPrincipal { }, "LIYOUMINGScheme"); AuthenticateResult result = AuthenticateResult.Success(ticket); return await Task.FromResult(result);
這里我舉個例子,返回的是AuthenticateResult包裹的AuthenticationTicket,AuthenticationTicket中包含了身份信息,當然還有HandleChallengeAsync、HandleForbiddenAsync、InitializeEventsAsync等就不做介紹了
其實算下加上擴展就4個類LIYOUMINGExtensions、LIYOUMINGHandler、LIYOUMINGOptions、LIYOUMINGPostConfigureOptions就基本上描述了
下面來體驗下:
在Configure中添加調試運行斷點進入了Handler中的HandleAuthenticateAsync,就實現了HandleAuthenticateAsync認證在手,天下你有,任你發揮你的能力
app.UseAuthentication(); app.Use(async (context, next) => { var user = context.User; if (user?.Identity?.IsAuthenticated ?? false) { await next(); } else { await context.ChallengeAsync(); } await context.Response.WriteAsync("Hello World!"); });
加深下,在LIYOUMINGHandler我在繼承接口IAuthenticationSignInHandler實現SignInAsync、SignOutAsync, 當然這里IAuthenticationSignInHandler繼承了IAuthenticationSignOutHandler、IAuthenticationHandler,所以這里可通過SignIn寫入信息了,下來來改造下代碼,能通過LIYOUMINGHandler進行簽入、簽出,細節就略了
public class LIYOUMINGHandler : AuthenticationHandler<LIYOUMINGOptions>,IAuthenticationSignInHandler { public Task SignInAsync(ClaimsPrincipal user, AuthenticationProperties properties) { return Task.CompletedTask; } public Task SignOutAsync(AuthenticationProperties properties) { return Task.CompletedTask; } }
app.Use(async (context, next) => { var user = context.User; if (user?.Identity?.IsAuthenticated ?? false) { await next(); } else { ClaimsIdentity claimsIdentity = new ClaimsIdentity(); claimsIdentity.AddClaim(new Claim("Test", "LIYOUMING")); ClaimsPrincipal claimsPrincipal = new ClaimsPrincipal(claimsIdentity); //簽入 await context.SignInAsync("LIYOUMINGScheme", claimsPrincipal); await context.Response.WriteAsync("SignInAsync"); } });
AddAuthentication中間件會先調用HandleAuthenticateAsync處理認證情況,其次上面代碼執行后,輸出了 SignInAsync,再次刷新的時候再次進入中間件的HandleAuthenticateAsync,這個時候處理認證信息 ,最后也沒沒有輸出