|
|
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<BasicAuthenticationOptions>
|
|
|
{
|
|
|
private readonly UTF8Encoding _utf8ValidatingEncoding = new UTF8Encoding(false, true);
|
|
|
|
|
|
public BasicAuthenticationHandler
|
|
|
(
|
|
|
IOptionsMonitor<BasicAuthenticationOptions> 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<object> CreateEventsAsync() => Task.FromResult<object>(new BasicAuthenticationEvents());
|
|
|
|
|
|
protected override async Task<AuthenticateResult> 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;
|
|
|
}
|
|
|
}
|
|
|
}
|