zoukankan      html  css  js  c++  java
  • .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,这个时候处理认证信息 ,最后也没没有输出

  • 相关阅读:
    Quickuse.Ioc 快速应用.依赖注入组件
    Quickuse.Utility 快速应用.基础组件
    对System.ComponentModel.DataAnnotations 的学习应用
    C# List 转 Tree 公共方法
    C# 用Redis实现的分布式锁
    使用DbTableColumnWeb项目简要
    Application_Error VS OnException 遇到的坑
    在使用Intelligencia.UrlRewriter过程中 中文乱码问题
    C# MVC 页面静态化导致的问题
    关于.NetCore与.Netframework 对于DataSet的序列化与反序列化问题的探讨.
  • 原文地址:https://www.cnblogs.com/liyouming/p/9922701.html
Copyright © 2011-2022 走看看