HTTP认证之基本认证——Basic(二) (2)

3.接下来,就是对认证过程处理的封装了,需要继承自Microsoft.AspNetCore.Authentication.AuthenticationHandler

public class BasicHandler : AuthenticationHandler<BasicOptions> { public BasicHandler(IOptionsMonitor<BasicOptions> options, ILoggerFactory logger, UrlEncoder encoder, ISystemClock clock) : base(options, logger, encoder, clock) { } protected new BasicEvents Events { get => (BasicEvents)base.Events; set => base.Events = value; } /// <summary> /// 确保创建的 Event 类型是 BasicEvents /// </summary> /// <returns></returns> protected override Task<object> CreateEventsAsync() => Task.FromResult<object>(new BasicEvents()); protected override async Task<AuthenticateResult> HandleAuthenticateAsync() { var credentials = GetCredentials(Request); if(credentials == null) { return AuthenticateResult.NoResult(); } try { credentials = Encoding.UTF8.GetString(Convert.FromBase64String(credentials)); var data = credentials.Split(':'); if(data.Length != 2) { return AuthenticateResult.Fail("Invalid credentials, error format."); } var validateCredentialsContext = new ValidateCredentialsContext(Context, Scheme, Options) { UserName = data[0], Password = data[1] }; await Events.ValidateCredentials(validateCredentialsContext); //认证通过 if(validateCredentialsContext.Result?.Succeeded == true) { var ticket = new AuthenticationTicket(validateCredentialsContext.Principal, Scheme.Name); return AuthenticateResult.Success(ticket); } return AuthenticateResult.NoResult(); } catch(FormatException) { return AuthenticateResult.Fail("Invalid credentials, error format."); } catch(Exception ex) { return AuthenticateResult.Fail(ex.Message); } } protected override async Task HandleChallengeAsync(AuthenticationProperties properties) { var authResult = await HandleAuthenticateOnceSafeAsync(); var challengeContext = new BasicChallengeContext(Context, Scheme, Options, properties) { AuthenticateFailure = authResult?.Failure }; await Events.Challenge(challengeContext); //质询已处理 if (challengeContext.Handled) return; var challengeValue = $"{ BasicDefaults.AuthenticationScheme } realm={ Options.Realm }"; var error = challengeContext.AuthenticateFailure?.Message; if(string.IsNullOrWhiteSpace(error)) { //将错误信息封装到内部 challengeValue += $" error={ error }"; } Response.StatusCode = (int)HttpStatusCode.Unauthorized; Response.Headers.Append(HeaderNames.WWWAuthenticate, challengeValue); } private string GetCredentials(HttpRequest request) { string credentials = null; string authorization = request.Headers[HeaderNames.Authorization]; //存在 Authorization 标头 if (authorization != null) { var scheme = BasicDefaults.AuthenticationScheme; if (authorization.StartsWith(scheme, StringComparison.OrdinalIgnoreCase)) { credentials = authorization.Substring(scheme.Length).Trim(); } } return credentials; } }

4.最后,就是要把封装的接口暴露给用户了,这里使用扩展方法的形式,虽然有4个方法,但实际上都是重载,是同一种行为。

public static class BasicExtensions { public static AuthenticationBuilder AddBasic(this AuthenticationBuilder builder) => builder.AddBasic(BasicDefaults.AuthenticationScheme, _ => { }); public static AuthenticationBuilder AddBasic(this AuthenticationBuilder builder, Action<BasicOptions> configureOptions) => builder.AddBasic(BasicDefaults.AuthenticationScheme, configureOptions); public static AuthenticationBuilder AddBasic(this AuthenticationBuilder builder, string authenticationScheme, Action<BasicOptions> configureOptions) => builder.AddBasic(authenticationScheme, displayName: null, configureOptions: configureOptions); public static AuthenticationBuilder AddBasic(this AuthenticationBuilder builder, string authenticationScheme, string displayName, Action<BasicOptions> configureOptions) => builder.AddScheme<BasicOptions, BasicHandler>(authenticationScheme, displayName, configureOptions); }

5.Basic认证库已经封装好了,我们创建一个ASP.NET Core WebApi程序来测试一下吧。

//在 ConfigureServices 中配置认证中间件 public void ConfigureServices(IServiceCollection services) { services.AddAuthentication(BasicDefaults.AuthenticationScheme) .AddBasic(options => { options.Realm = "Test Realm"; options.Events = new BasicEvents { OnValidateCredentials = context => { var user = UserService.Authenticate(context.UserName, context.Password); if (user != null) { //将用户信息封装到HttpContext var claim = new Claim(ClaimTypes.Name, context.UserName); var identity = new ClaimsIdentity(BasicDefaults.AuthenticationScheme); identity.AddClaim(claim); context.Principal = new ClaimsPrincipal(identity); context.Success(); } return Task.CompletedTask; } }; }); } //在 Configure 中启用认证中间件 public void Configure(IApplicationBuilder app, IHostingEnvironment env) { app.UseAuthentication(); }

对了,一定要记得为需要认证的Action添加[Authorize]特性,否则前面做的一切都是徒劳+_+

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/wpysyp.html