feat: 更新

main
bicijinlian 2 years ago
parent 53eab0dabf
commit c65317f18f

@ -1,4 +1,4 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Text; using System.Text;
@ -17,8 +17,6 @@ namespace AuthStudy.Authentication.Basic
{ {
public class BasicAuthenticationHandler : AuthenticationHandler<BasicAuthenticationOptions> public class BasicAuthenticationHandler : AuthenticationHandler<BasicAuthenticationOptions>
{ {
private const string _Scheme = "BasicScheme";
private readonly UTF8Encoding _utf8ValidatingEncoding = new UTF8Encoding(false, true); private readonly UTF8Encoding _utf8ValidatingEncoding = new UTF8Encoding(false, true);
public BasicAuthenticationHandler public BasicAuthenticationHandler
@ -42,69 +40,21 @@ namespace AuthStudy.Authentication.Basic
protected override async Task<AuthenticateResult> HandleAuthenticateAsync() protected override async Task<AuthenticateResult> HandleAuthenticateAsync()
{ {
string? authorizationHeader = Request.Headers["Authorization"]; string requestBasicText = GetRequestBasic();
if (string.IsNullOrEmpty(authorizationHeader))
{
return AuthenticateResult.NoResult();
}
// Exact match on purpose, rather than using string compare //认证信息
// asp.net request parsing will always trim the header and remove trailing spaces var basicInfo = ParseBasicInfo(requestBasicText);
if (_Scheme == authorizationHeader) if (!basicInfo.ParseResult)
{ {
const string noCredentialsMessage = "Authorization scheme was Basic but the header had no credentials."; return AuthenticateResult.Fail(basicInfo.Message);
Logger.LogInformation(noCredentialsMessage);
return AuthenticateResult.Fail(noCredentialsMessage);
} }
if (!authorizationHeader.StartsWith(_Scheme + ' ', StringComparison.OrdinalIgnoreCase))
{
return AuthenticateResult.NoResult();
}
string encodedCredentials = authorizationHeader.Substring(_Scheme.Length).Trim();
try try
{ {
string decodedCredentials = string.Empty;
byte[] base64DecodedCredentials;
try
{
base64DecodedCredentials = Convert.FromBase64String(encodedCredentials);
}
catch (FormatException)
{
const string failedToDecodeCredentials = "Cannot convert credentials from Base64.";
Logger.LogInformation(failedToDecodeCredentials);
return AuthenticateResult.Fail(failedToDecodeCredentials);
}
try
{
decodedCredentials = _utf8ValidatingEncoding.GetString(base64DecodedCredentials);
}
catch (Exception ex)
{
const string failedToDecodeCredentials = "Cannot build credentials from decoded base64 value, exception {ex.Message} encountered.";
Logger.LogInformation(failedToDecodeCredentials, ex.Message);
return AuthenticateResult.Fail(ex.Message);
}
var delimiterIndex = decodedCredentials.IndexOf(":", StringComparison.OrdinalIgnoreCase);
if (delimiterIndex == -1)
{
const string missingDelimiterMessage = "Invalid credentials, missing delimiter.";
Logger.LogInformation(missingDelimiterMessage);
return AuthenticateResult.Fail(missingDelimiterMessage);
}
var username = decodedCredentials.Substring(0, delimiterIndex);
var password = decodedCredentials.Substring(delimiterIndex + 1);
var validateCredentialsContext = new ValidateCredentialsContext(Context, Scheme, Options) var validateCredentialsContext = new ValidateCredentialsContext(Context, Scheme, Options)
{ {
Username = username, Username = basicInfo.UserName,
Password = password Password = basicInfo.Password
}; };
if (Events != null) if (Events != null)
@ -113,10 +63,9 @@ namespace AuthStudy.Authentication.Basic
} }
if (validateCredentialsContext.Result != null && if (validateCredentialsContext.Result != null && validateCredentialsContext.Result.Succeeded)
validateCredentialsContext.Result.Succeeded)
{ {
var ticket = new AuthenticationTicket(validateCredentialsContext.Principal, Scheme.Name); var ticket = new AuthenticationTicket(validateCredentialsContext.Principal!, Scheme.Name);
return AuthenticateResult.Success(ticket); return AuthenticateResult.Success(ticket);
} }
@ -151,9 +100,16 @@ namespace AuthStudy.Authentication.Basic
protected override Task HandleChallengeAsync(AuthenticationProperties properties) protected override Task HandleChallengeAsync(AuthenticationProperties properties)
{ {
//如果响应已经开始,则忽略
//比如同时使用其它认证,失败后已经被设置了
if (Response.HasStarted)
{
return Task.CompletedTask;
}
if (!Request.IsHttps && !Options.AllowInsecureProtocol) if (!Request.IsHttps && !Options.AllowInsecureProtocol)
{ {
const string insecureProtocolMessage = "Request is HTTP, Basic Authentication will not respond."; const string insecureProtocolMessage = "请求为HTTP基本身份验证将不响应。";
Logger.LogInformation(insecureProtocolMessage); Logger.LogInformation(insecureProtocolMessage);
Response.StatusCode = StatusCodes.Status421MisdirectedRequest; Response.StatusCode = StatusCodes.Status421MisdirectedRequest;
} }
@ -162,12 +118,106 @@ namespace AuthStudy.Authentication.Basic
Response.StatusCode = 401; Response.StatusCode = 401;
if (!Options.SuppressWWWAuthenticateHeader) if (!Options.SuppressWWWAuthenticateHeader)
{ {
var headerValue = _Scheme + $" realm=\"{Options.Realm}\""; var headerValue = BasicAuthenticationDefaults.AuthenticationScheme + $" realm=\"{Options.Realm}\"";
Response.Headers.Append(HeaderNames.WWWAuthenticate, headerValue); Response.Headers.Append(HeaderNames.WWWAuthenticate, headerValue);
} }
} }
return Task.CompletedTask; 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;
}
} }
} }

@ -61,7 +61,7 @@ namespace AuthStudy.Authentication.Basic
public bool AllowInsecureProtocol public bool AllowInsecureProtocol
{ {
get; set; get; set;
} } = true;
/// <summary> /// <summary>
/// 事件 /// 事件

@ -1,6 +1,7 @@
using AuthStudy.Authentication.Browser; using AuthStudy.Authentication.Browser;
using AuthStudy.WebApp.VModels; using AuthStudy.WebApp.VModels;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
@ -16,14 +17,18 @@ namespace AuthStudy.WebApp.Controllers
} }
//多特性是and特性内逗号分隔是or
//[Authorize]
//[Authorize(AuthenticationSchemes = AuthenticationSchemeList.BaseBrowserScheme)] //[Authorize(AuthenticationSchemes = AuthenticationSchemeList.BaseBrowserScheme)]
//[Authorize(AuthenticationSchemes = AuthenticationSchemeList.BrowserScheme)] //[Authorize(AuthenticationSchemes = AuthenticationSchemeList.BrowserScheme)]
//[Authorize(AuthenticationSchemes = AuthenticationSchemeList.BasicScheme)] //[Authorize(AuthenticationSchemes = AuthenticationSchemeList.BasicScheme)]
[Authorize(AuthenticationSchemes = $"{AuthenticationSchemeList.BaseBrowserScheme},{AuthenticationSchemeList.BrowserScheme},{AuthenticationSchemeList.BasicScheme}")] //[Authorize(AuthenticationSchemes = $"{AuthenticationSchemeList.BrowserScheme},{AuthenticationSchemeList.BasicScheme}")]
//[Authorize] //[Authorize(AuthenticationSchemes = $"{AuthenticationSchemeList.BaseBrowserScheme},{AuthenticationSchemeList.BrowserScheme},{AuthenticationSchemeList.BasicScheme}")]
[HttpGet] [HttpGet]
public IActionResult GetAll() public async Task<IActionResult> GetAll()
{ {
var dd = await HttpContext.AuthenticateAsync();
//输出认证信息 //输出认证信息
foreach (var claim in User.Claims) foreach (var claim in User.Claims)
{ {

@ -1,5 +1,8 @@
using System.Security.Claims;
using AuthStudy.Authentication.Basic; using AuthStudy.Authentication.Basic;
using AuthStudy.Authentication.Basic.Events;
using AuthStudy.Authentication.Browser; using AuthStudy.Authentication.Browser;
namespace AuthStudy.WebApp namespace AuthStudy.WebApp
@ -34,7 +37,30 @@ namespace AuthStudy.WebApp
option.AllowBrowsers = new List<string>() { "Edge", "Chrome", "Firefox" }; option.AllowBrowsers = new List<string>() { "Edge", "Chrome", "Firefox" };
}) })
//基本认证 //基本认证
.AddBasic(AuthenticationSchemeList.BasicScheme); .AddBasic(options =>
{
options.Realm = "Basic Authentication";
options.Events = new BasicAuthenticationEvents
{
OnValidateCredentials = context =>
{
if (context.Username == context.Password)
{
var claims = new[]
{
new Claim(ClaimTypes.NameIdentifier, context.Username, ClaimValueTypes.String, context.Options.ClaimsIssuer),
new Claim(ClaimTypes.Name, context.Username, ClaimValueTypes.String, context.Options.ClaimsIssuer)
};
context.Principal = new ClaimsPrincipal(new ClaimsIdentity(claims, context.Scheme.Name));
context.Success();
}
return Task.CompletedTask;
}
};
});
//默认基类实现注册 //默认基类实现注册
@ -52,6 +78,11 @@ namespace AuthStudy.WebApp
app.MapControllers(); app.MapControllers();
app.Run(); app.Run();
void Test()
{
}
} }
} }
} }
Loading…
Cancel
Save