.NetCore源碼閱讀筆記系列之Security (二) 自定義認證實踐


通過前面對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,這個時候處理認證信息 ,最后也沒沒有輸出


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM