using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Text.Encodings.Web; using System.Threading.Tasks; using AuthStudy.Authentication.Basic.Events; using Microsoft.AspNetCore.Authentication; using Microsoft.AspNetCore.Http; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; using Microsoft.Net.Http.Headers; namespace AuthStudy.Authentication.Basic { public class BasicAuthenticationHandler : AuthenticationHandler { private readonly UTF8Encoding _utf8ValidatingEncoding = new UTF8Encoding(false, true); public BasicAuthenticationHandler ( IOptionsMonitor options, ILoggerFactory logger, UrlEncoder encoder, ISystemClock clock ) : base(options, logger, encoder, clock) { } protected new BasicAuthenticationEvents? Events { get { return (BasicAuthenticationEvents?)base.Events; } set { base.Events = value; } } protected override Task CreateEventsAsync() => Task.FromResult(new BasicAuthenticationEvents()); protected override async Task HandleAuthenticateAsync() { string requestBasicText = GetRequestBasic(); //认证信息 var basicInfo = ParseBasicInfo(requestBasicText); if (!basicInfo.ParseResult) { return AuthenticateResult.Fail(basicInfo.Message); } try { var validateCredentialsContext = new ValidateCredentialsContext(Context, Scheme, Options) { Username = basicInfo.UserName, Password = basicInfo.Password }; if (Events != null) { await Events.ValidateCredentials(validateCredentialsContext); } if (validateCredentialsContext.Result != null && validateCredentialsContext.Result.Succeeded) { var ticket = new AuthenticationTicket(validateCredentialsContext.Principal!, Scheme.Name); return AuthenticateResult.Success(ticket); } if (validateCredentialsContext.Result != null && validateCredentialsContext.Result.Failure != null) { return AuthenticateResult.Fail(validateCredentialsContext.Result.Failure); } return AuthenticateResult.NoResult(); } catch (Exception ex) { var authenticationFailedContext = new BasicAuthenticationFailedContext(Context, Scheme, Options) { Exception = ex }; if (Events != null) { await Events.AuthenticationFailed(authenticationFailedContext).ConfigureAwait(true); } if (authenticationFailedContext.Result != null) { return authenticationFailedContext.Result; } throw; } } protected override Task HandleChallengeAsync(AuthenticationProperties properties) { //如果响应已经开始,则忽略 //比如同时使用其它认证,失败后已经被设置了 if (Response.HasStarted) { return Task.CompletedTask; } if (!Request.IsHttps && !Options.AllowInsecureProtocol) { const string insecureProtocolMessage = "请求为HTTP,基本身份验证将不响应。"; Logger.LogInformation(insecureProtocolMessage); Response.StatusCode = StatusCodes.Status421MisdirectedRequest; } else { Response.StatusCode = 401; if (!Options.SuppressWWWAuthenticateHeader) { var headerValue = BasicAuthenticationDefaults.AuthenticationScheme + $" realm=\"{Options.Realm}\""; Response.Headers.Append(HeaderNames.WWWAuthenticate, headerValue); } } return Task.CompletedTask; } private string GetRequestBasic() { //请求头接收 string basicText = Request.Headers["Authorization"].ToString(); if (string.IsNullOrWhiteSpace(basicText)) { basicText = Request.Headers["Authorization"].ToString(); } //URL参数 if (string.IsNullOrWhiteSpace(basicText)) { basicText = $"{Request.Query["Basic"]}"; } //URL参数 if (string.IsNullOrWhiteSpace(basicText)) { basicText = $"{Request.Query["User"]}:{Request.Query["Password"]}"; basicText = basicText.TrimStart(':'); } return basicText; } private (bool ParseResult, string Message, string UserName, string Password) ParseBasicInfo(string requestBasic) { (bool ParseResult, string Message, string UserName, string Password) result = (true, "格式错误", "", ""); if (string.IsNullOrWhiteSpace(requestBasic)) { result.Message = "无认证信息"; return result; } requestBasic = requestBasic.Trim(); if (requestBasic.Equals("Basic", StringComparison.OrdinalIgnoreCase)) { result.Message = "授权方案是Basic,但标头没有内容!"; return result; } var basicList = requestBasic.Split(':'); if (basicList.Length == 2) { result.ParseResult = true; result.Message = "认证信息正确"; result.UserName = basicList[0]; result.Password = basicList[1]; return result; } requestBasic = requestBasic.Replace("Basic ", "", StringComparison.OrdinalIgnoreCase); string decodedCredentials = string.Empty; byte[] base64DecodedCredentials; try { base64DecodedCredentials = Convert.FromBase64String(requestBasic); } catch (FormatException) { result.ParseResult = false; result.Message = "凭据不是Base64格式"; return result; } try { decodedCredentials = _utf8ValidatingEncoding.GetString(base64DecodedCredentials); } catch (Exception ex) { result.ParseResult = false; result.Message = $"认证信息生成凭据时异常:{ex.Message}"; return result; } var list = decodedCredentials.Split(':'); if (list.Length != 2) { result.ParseResult = false; result.Message = $"凭据无效,缺少分隔符!"; return result; } result.ParseResult = true; result.Message = "认证信息正确"; result.UserName = list[0]; result.Password = list[1]; return result; } } }