.NET CORE之Authentication

  This article in order to achieve a Basic authentication how to achieve certification under the following .NET CORE to understand.

  First, be sure that the implementation is based on Middlerware certification pipeline implementation, the official source address: https://github.com/aspnet/Security . You can see the official has been achieved jwt, oauth, google and many other third-party certification, the principle we are not presented here today.

    Let's implement Basic authentication.

  AuthenticationSchemeOptions.  Responsible for initialization parameters. Here we need an extra delegate user authentication. code show as below:

    public class BasicOption : AuthenticationSchemeOptions
    {
        public BasicOption()
            : base()
        {
            Events = new BasicEvents();
        }

        public Func<string, string, bool> ValidateUser { get; set; }
        public new BasicEvents Events
        {
            get { return (BasicEvents)base.Events; }
            set { base.Events = value; }
        }
    }

  BasicDefault. Define some basic constants

    public static class BasicDefault
    {
        public const string AuthenticationScheme = "Basic";
        public const string DisplayName = "Basic";
    }

  ResultContext <T>. Extended authentication process for context

    public class BasicTokenValidatedContext : ResultContext<BasicOption>
    {
        public BasicTokenValidatedContext(HttpContext context, AuthenticationScheme scheme, BasicOption options)
            : base(context, scheme, options)
        {
        }

    }

  BasicEvents. Certification process for all types of custom event trigger, where we define the verification after a successful event for client customizations

    public class BasicEvents
    {
        public Func<BasicTokenValidatedContext, Task> OnTokenValidated { get; set; } = context => Task.CompletedTask;

        public virtual Task TokenValidated(BasicTokenValidatedContext context) => OnTokenValidated(context);
    }

  AuthenticationHandler <T>. Here the core part of the certification process, HandleAuthenticateAsync for authentication processing. HandleChallengeAsync for processing authentication failure subsequent Challenge

    public class BasicHandler : AuthenticationHandler<BasicOption>
    {
        private const string KEY_AUTHORIZATION = "authorization";
        private const string KEY_SPLIT = ":";

        protected new BasicEvents Events
        {
            get => (BasicEvents)base.Events;
            set => base.Events = value;
        }

        public BasicHandler(IOptionsMonitor<BasicOption> options, ILoggerFactory logger, UrlEncoder encoder, ISystemClock clock)
            : base(options, logger, encoder, clock)
        {
        }

        protected override async Task<AuthenticateResult> HandleAuthenticateAsync()
        {
            string authorization = Request.Headers[KEY_AUTHORIZATION];
            if (string.IsNullOrEmpty(authorization))
            {
                Logger.LogInformation("请求头authorization为空,目标路径{0}", Request.Path);
                return AuthenticateResult.NoResult();
            }
            string token = string.Empty;
            if (authorization.StartsWith(BasicDefault.AuthenticationScheme + " ", StringComparison.CurrentCultureIgnoreCase))
            {
                token = authorization.Substring(BasicDefault.AuthenticationScheme.Length).Trim();
            }
            if (string.IsNullOrEmpty(token))
            {
                Logger.LogInformation("无效的请求头authorization,目标路径{0}", Request.Path);
                return AuthenticateResult.NoResult();
            }

            var checkUser = Options.ValidateUser;
            if (checkUser == null)
            {
                Logger.LogInformation("Basic TokenValidator不能,目标路径{0}", Request.Path);
                return await Task.FromResult(AuthenticateResult.NoResult());
            }

            try
            {
                var data = Encoding.UTF8.GetString(Convert.FromBase64String(token));
                if (string.IsNullOrEmpty(data)) throw new Exception("basic token 格式错误");

                string[] array = data.Split(KEY_SPLIT.ToCharArray());
                if (array.Length != 2) throw new Exception("basic token 格式错误");

                var username = array[0];
                var password = array[1];
                if (string.IsNullOrEmpty(username) || string.IsNullOrEmpty(password)) throw new Exception("basic token 格式错误");

                if (!checkUser(username, password))
                {
                    Logger.LogInformation("token 验证失败");
                    return AuthenticateResult.Fail("token 验证失败");
                }

                var claims = new List<Claim>()
                {
                    new Claim(ClaimTypes.Name, username)
                };

                var principer = new ClaimsPrincipal(new ClaimsIdentity(claims, BasicDefault.AuthenticationScheme));
                var validatedContext = new BasicTokenValidatedContext(Context, Scheme, Options)
                {
                    Principal = principer
                };

                await Events.TokenValidated(validatedContext);

                validatedContext.Success();

                return validatedContext.Result;
            }
            catch (Exception ex)
            {
                Logger.LogDebug(token + " validate failed: " + ex.Message);
                return AuthenticateResult.Fail(ex.Message);
            }

        }

        protected override async Task HandleChallengeAsync(AuthenticationProperties properties)
        {
            var authResult = await HandleAuthenticateOnceSafeAsync();

            Response.Headers.Add(HeaderNames.WWWAuthenticate, BasicDefault.AuthenticationScheme);
            Response.StatusCode = 401;
            if (authResult.Failure != null && !string.IsNullOrEmpty(authResult.Failure.Message))
            {
                var byteMsg = System.Text.Encoding.Default.GetBytes(authResult.Failure.Message);
                Response.Body.Write(byteMsg, 0, byteMsg.Length);
            }

            await base.HandleChallengeAsync(properties);
        }
    }

  BasicExtensions. To provide a method for registering the .NET CORE.

    public static class BasicExtensions
    {
        public static AuthenticationBuilder AddBasic(this AuthenticationBuilder builder)
            => builder.AddBasic(BasicDefault.AuthenticationScheme, _ => { });

        public static AuthenticationBuilder AddBasic(this AuthenticationBuilder builder, Action<BasicOption> configureOptions)
            => builder.AddBasic(BasicDefault.AuthenticationScheme, configureOptions);

        public static AuthenticationBuilder AddBasic(this AuthenticationBuilder builder, string authenticationScheme, Action<BasicOption> configureOptions)
            => builder.AddBasic(authenticationScheme, displayName: BasicDefault.DisplayName, configureOptions: configureOptions);

        public static AuthenticationBuilder AddBasic(this AuthenticationBuilder builder, string authenticationScheme, string displayName, Action<BasicOption> configureOptions)
        {
            return builder.AddScheme<BasicOption, BasicHandler>(authenticationScheme, displayName, configureOptions);
        }
    }

  These are all complete Basic authentication methods. We found that actually did not involve any Middlerware part. The official reason is to achieve a real default Authentication, which has a  collection of IAuthenticationRequestHandler, we created  AuthenticationHandler <T> extension will be added to the collection, Authentication will be responsible for the collection of each Handler for processing. Source part as follows:

            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;

                }

            }

  Finally, we will register Basic authentication in the .NET CORE

            services.AddAuthentication(BasicDefault.AuthenticationScheme)
                .AddBasic(options =>
                {
                    options.ValidateUser = (username, password) =>
                    {
                        var clients = Configuration.GetSection("Clients").Get<List<ClientOptions>>();
                        if (clients == null || clients.Count == 0) return false;

                        return clients.Exists(x => string.Equals(x.Appkey, username, StringComparison.CurrentCultureIgnoreCase)
                                                                    && string.Equals(x.Appsecret, password, StringComparison.CurrentCultureIgnoreCase));
                    };
                    //options.Events = new AspNetCore.Authentication.Basic.Events.BasicEvents();
                    options.Events.OnTokenValidated = context =>
                    {
                        if (context.Principal.Identity.IsAuthenticated)
                        {
                            var clients = Configuration.GetSection("Clients").Get<List<ClientOptions>>();
                            if (clients == null || clients.Count == 0) return Task.CompletedTask;
                            var appkey = context.Principal.Identity.Name;
                            var actions = clients.Single(x => string.Equals(x.Appkey, appkey, StringComparison.CurrentCultureIgnoreCase)).Actions;
                            context.Properties.SetParameter("actions", actions);
                        }

                        return Task.CompletedTask;
                    };
                });

  Do not forget to join in the Configure method 

app.UseAuthentication();

  Well, we completed the Basic authentication ~~

       Follow-up questions, found during the test even if authentication is not passed, then action can be a normal visit, Authorize need to meet in order to trigger Challenge. Here I Authentication and Authorization added a little puzzled, stands to reason that the former is responsible for identifying the user, which is responsible for confirming the user privileges, but if the user is recognized as illegal under the circumstances, why wait until Authorization to deal with this? ?

  

Guess you like

Origin www.cnblogs.com/gt1987/p/11011754.html