--
/// <summary>
/// Base class for the per-request work performed by most authentication middleware.
/// 抽象基类:身份验证中间件每次请求要完成的工作
/// </summary>
public abstract class AuthenticationHandler : IAuthenticationHandler
{
private static readonly RandomNumberGenerator CryptoRandom = RandomNumberGenerator.Create();
#region -----private public property
private Task<AuthenticationTicket> _authenticate;
private bool _authenticateInitialized;
private object _authenticateSyncLock;
private Task _applyResponse;
private bool _applyResponseInitialized;
private object _applyResponseSyncLock;
private AuthenticationOptions _baseOptions;
/// <summary>
/// 两个属性:AuthenticationTypes Properties
/// 一个方法: void Accept(string authenticationType, IDictionary<string, object> description);
/// </summary>
protected IChallengeContext ChallengeContext { get; set; }
/// <summary>
/// 两个属性:ClaimsIdentity Identity { get; private set; }
/// AuthenticationProperties Properties { get; private set; }
/// </summary>
protected SignInIdentityContext SignInIdentityContext { get; set; }
/// <summary>
/// 一个属性:IEnumerable<string> AuthenticationTypes { get; }
/// 一个方法: void Accept(string authenticationType, IDictionary<string, object> description);
/// </summary>
protected ISignOutContext SignOutContext { get; set; }
protected HttpContext Context { get; private set; }
protected HttpRequest Request
{
get { return Context.Request; }
}
protected HttpResponse Response
{
get { return Context.Response; }
}
protected PathString RequestPathBase { get; private set; }
/// <summary>
/// 三个属性:string AuthenticationType
/// AuthenticationMode AuthenticationMode { get; set; } = AuthenticationMode.Active;
/// public AuthenticationDescription Description { get; set; } = new AuthenticationDescription();
/// </summary>
internal AuthenticationOptions BaseOptions
{
get { return _baseOptions; }
}
/// <summary>
/// 之前的Handler 默认是NULL
/// </summary>
public IAuthenticationHandler PriorHandler { get; set; }
/// <summary>
/// 是否发生了异常
/// </summary>
public bool Faulted { get; set; }
#endregion
public virtual void GetDescriptions(IAuthTypeContext authTypeContext)
{
/*
接口实现类: public class AuthTypeContext : IAuthTypeContext
public IEnumerable<AuthenticationDescription> Results
{
get { return _results; }
}
public void Accept(IDictionary<string, object> description)
{
_results.Add(new AuthenticationDescription(description));
}
*/
authTypeContext.Accept(BaseOptions.Description.Dictionary);
if (PriorHandler != null)
{
PriorHandler.GetDescriptions(authTypeContext);
}
}
// 谁来调用此方法: Response.SignIn
public virtual void SignIn(ISignInContext context)
{
ClaimsIdentity identity;
// 举例CookieAuthentication AuthenticationType = "Cookies";
//OAuth 可以是:GitHub-AccessToken Google-AccessToken ;Microsoft-AccessToken 等;
// 判断是否存在这种验证类型
if (SecurityHelper.LookupSignIn(context.Identities, BaseOptions.AuthenticationType, out identity))
{
SignInIdentityContext = new SignInIdentityContext(identity, new AuthenticationProperties(context.Properties));
SignOutContext = null;
/*
接口实现类: public class SignInContext : ISignInContext{}
private List<string> _accepted=new List<string>();
_accepted.Add(authenticationType);
*/
context.Accept(BaseOptions.AuthenticationType, BaseOptions.Description.Dictionary);
}
if (PriorHandler != null)
{
PriorHandler.SignIn(context);
}
}
// 谁来调用此方法: Response.SignOut
public virtual void SignOut(ISignOutContext context)
{
// 主动的话可以没有Type
if (SecurityHelper.LookupSignOut(context.AuthenticationTypes, BaseOptions.AuthenticationType, BaseOptions.AuthenticationMode))
{
SignInIdentityContext = null;
SignOutContext = context;
/*
private List<string> _accepted=new List<string>();
_accepted.Add(authenticationType);
*/
context.Accept(BaseOptions.AuthenticationType, BaseOptions.Description.Dictionary);
}
if (PriorHandler != null)
{
PriorHandler.SignOut(context);
}
}
/// <summary>
/// 谁来调用此方法: Response.Challenge
/// </summary>
/// <param name="context"></param>
public virtual void Challenge(IChallengeContext context)
{
// 主动的话可以没有Type
if (SecurityHelper.LookupChallenge(context.AuthenticationTypes, BaseOptions.AuthenticationType, BaseOptions.AuthenticationMode))
{
ChallengeContext = context;
/*
private List<string> _accepted=new List<string>();
_accepted.Add(authenticationType);
*/
context.Accept(BaseOptions.AuthenticationType, BaseOptions.Description.Dictionary);
}
if (PriorHandler != null)
{
PriorHandler.Challenge(context);
}
}
private void RegisterAuthenticationHandler()
{
var auth = Context.GetAuthentication();
PriorHandler = auth.Handler;
auth.Handler = this;
}
/// <summary>
/// 这个方法什么时候调用 执行到中间件Invoke的时候会先初始化
/// 如: app.UseCookieAuthentication( options =>{} );
/// </summary>
/// <param name="options"></param>
/// <param name="context"></param>
/// <returns></returns>
protected async Task BaseInitializeAsync(AuthenticationOptions options, HttpContext context)
{
_baseOptions = options;
Context = context;
RequestPathBase = Request.PathBase;
/*
var auth = Context.GetAuthentication();
PriorHandler = auth.Handler; 原来的Handler 默认是NULL
auth.Handler = this; -----------这里是关键
*/
RegisterAuthenticationHandler();
/*
public override void OnSendingHeaders(Action<object> callback, object state)
{
HttpResponseFeature.OnSendingHeaders(callback, state);
}
register(callback, state);
*/
/*
// 调用 abstract 方法 需要子类实现 ApplyResponseGrant();
// 调用 abstract 方法 需要子类实现 ApplyResponseChallenge();
handler.ApplyResponse();
*/
Response.OnSendingHeaders(OnSendingHeaderCallback, this);
/*
protected virtual Task InitializeCoreAsync()
{
return Task.FromResult(0);
}
*/
await InitializeCoreAsync();
// 默认是 Active 会修改 context.User = newClaimsPrincipal;
if (BaseOptions.AuthenticationMode == AuthenticationMode.Active)
{
// protected abstract AuthenticationTicket AuthenticateCore();
AuthenticationTicket ticket = await AuthenticateAsync();
if (ticket != null)
{
if (ticket.Identity != null)
{
/*
public static void AddUserIdentity([NotNull] HttpContext context, [NotNull] IIdentity identity)
{
var newClaimsPrincipal = new ClaimsPrincipal(identity);
ClaimsPrincipal existingPrincipal = context.User;
if (existingPrincipal != null)
{
foreach (var existingClaimsIdentity in existingPrincipal.Identities)
{
if (existingClaimsIdentity.IsAuthenticated)
{
newClaimsPrincipal.AddIdentity(existingClaimsIdentity);
}
}
}
context.User = newClaimsPrincipal;
}
*/
SecurityHelper.AddUserIdentity(Context, ticket.Identity);
}
else if (ticket.Principal != null)
{ // 方法同上
SecurityHelper.AddUserIdentity(Context, ticket.Principal.Identity);
}
}
}
}
private static void OnSendingHeaderCallback(object state)
{
AuthenticationHandler handler = (AuthenticationHandler)state;
// 调用 Virtual 方法 需要子类实现 ApplyResponseGrant();
// 调用 Virtual 方法 需要子类实现 ApplyResponseChallenge();
handler.ApplyResponse();
}
protected virtual Task InitializeCoreAsync()
{
return Task.FromResult(0);
}
/// <summary>
/// Called once per request after Initialize and Invoke.
/// 初始化和 Invoke 后被调用;
/// </summary>
/// <returns>async completion</returns>
internal async Task TeardownAsync()
{
try
{
//abstract await ApplyResponseGrantAsync();
//abstract await ApplyResponseChallengeAsync();
await ApplyResponseAsync();
}
catch (Exception)
{
try
{
//// virtual 空方法
await TeardownCoreAsync();
}
catch (Exception)
{
// Don't mask the original exception
}
UnregisterAuthenticationHandler();
throw;
}
// virtual 空方法
await TeardownCoreAsync();
/*
var auth = Context.GetAuthentication();
auth.Handler = PriorHandler;
*/
UnregisterAuthenticationHandler();
}
protected virtual Task TeardownCoreAsync()
{
return Task.FromResult(0);
}
/// <summary>
/// Called once by common code after initialization. If an authentication middleware responds directly to
/// specifically known paths it must override this virtual, compare the request path to it's known paths,
/// provide any response information as appropriate, and true to stop further processing.
/// 初始化后被调用.
/// </summary>
/// <returns>Returning false will cause the common code to call the next middleware in line. Returning true will
/// cause the common code to begin the async completion journey without calling the rest of the middleware
/// pipeline.
/// 返回 false 会调用后续的中间件。 返回 true 会导致公共代码 异步完成 而不再调用后面的中间件。
/// </returns>
public virtual Task<bool> InvokeAsync()
{
return Task.FromResult<bool>(false);
}
public virtual void Authenticate(IAuthenticateContext context)
{
if (context.AuthenticationTypes.Contains(BaseOptions.AuthenticationType, StringComparer.Ordinal))
{
// 调用需要子类实现的方法
// protected abstract AuthenticationTicket AuthenticateCore();
AuthenticationTicket ticket = Authenticate();
if (ticket != null && ticket.Identity != null)
{
/*
var descrip = new AuthenticationDescription(description);
_accepted.Add(descrip.AuthenticationType); // may not match identity.AuthType
_results.Add(new AuthenticationResult(identity, new AuthenticationProperties(properties), descrip));
*/
context.Authenticated(ticket.Identity, ticket.Properties.Dictionary, BaseOptions.Description.Dictionary);
}
else
{
// _accepted.Add(authenticationType);
context.NotAuthenticated(BaseOptions.AuthenticationType, properties: null, description: BaseOptions.Description.Dictionary);
}
}
if (PriorHandler != null)
{
PriorHandler.Authenticate(context);
}
}
public virtual async Task AuthenticateAsync(IAuthenticateContext context)
{
if (context.AuthenticationTypes.Contains(BaseOptions.AuthenticationType, StringComparer.Ordinal))
{
AuthenticationTicket ticket = await AuthenticateAsync();
if (ticket != null && ticket.Identity != null)
{
context.Authenticated(ticket.Identity, ticket.Properties.Dictionary, BaseOptions.Description.Dictionary);
}
else
{
context.NotAuthenticated(BaseOptions.AuthenticationType, properties: null, description: BaseOptions.Description.Dictionary);
}
}
if (PriorHandler != null)
{
await PriorHandler.AuthenticateAsync(context);
}
}
public AuthenticationTicket Authenticate()
{
// 确保只执行一次
return LazyInitializer.EnsureInitialized(
ref _authenticate,
ref _authenticateInitialized,
ref _authenticateSyncLock,
() =>
{
return Task.FromResult(AuthenticateCore());
}).GetAwaiter().GetResult();
}
protected abstract AuthenticationTicket AuthenticateCore();
/// <summary>
/// Causes the authentication logic in AuthenticateCore to be performed for the current request
/// at most once and returns the results. Calling Authenticate more than once will always return
/// the original value.
///
/// This method should always be called instead of calling AuthenticateCore directly.
/// </summary>
/// <returns>The ticket data provided by the authentication logic</returns>
public Task<AuthenticationTicket> AuthenticateAsync()
{
return LazyInitializer.EnsureInitialized(
ref _authenticate,
ref _authenticateInitialized,
ref _authenticateSyncLock,
AuthenticateCoreAsync);
}
/// <summary>
/// The core authentication logic which must be provided by the handler. Will be invoked at most
/// once per request. Do not call directly, call the wrapping Authenticate method instead.
/// </summary>
/// <returns>The ticket data provided by the authentication logic</returns>
protected virtual Task<AuthenticationTicket> AuthenticateCoreAsync()
{
return Task.FromResult(AuthenticateCore());
}
private void ApplyResponse()
{
// If ApplyResponse already failed in the OnSendingHeaderCallback or TeardownAsync code path then a
// failed task is cached. If called again the same error will be re-thrown. This breaks error handling
// scenarios like the ability to display the error page or re-execute the request.
try
{
if (!Faulted)
{
LazyInitializer.EnsureInitialized(
ref _applyResponse,
ref _applyResponseInitialized,
ref _applyResponseSyncLock,
() =>
{
ApplyResponseCore();
return Task.FromResult(0);
}).GetAwaiter().GetResult(); // Block if the async version is in progress.
}
}
catch (Exception)
{
Faulted = true;
throw;
}
}
protected virtual void ApplyResponseCore()
{
ApplyResponseGrant();
ApplyResponseChallenge();
}
/// <summary>
/// Causes the ApplyResponseCore to be invoked at most once per request. This method will be
/// invoked either earlier, when the response headers are sent as a result of a response write or flush,
/// or later, as the last step when the original async call to the middleware is returning.
/// </summary>
/// <returns></returns>
private async Task ApplyResponseAsync()
{
// If ApplyResponse already failed in the OnSendingHeaderCallback or TeardownAsync code path then a
// failed task is cached. If called again the same error will be re-thrown. This breaks error handling
// scenarios like the ability to display the error page or re-execute the request.
try
{
if (!Faulted)
{
await LazyInitializer.EnsureInitialized(
ref _applyResponse,
ref _applyResponseInitialized,
ref _applyResponseSyncLock,
ApplyResponseCoreAsync);
}
}
catch (Exception)
{
Faulted = true;
throw;
}
}
/// <summary>
/// Core method that may be overridden by handler. The default behavior is to call two common response
/// activities, one that deals with sign-in/sign-out concerns, and a second to deal with 401 challenges.
/// 默认的行为时调用连个响应活动 一个 处理 登入 登出 如 设置 删除Cookie
/// 另一个处理 401 未授权 质询
/// 两个地方调用 TrearDown ON 另一个 是 OnSendingHeaderCallback
/// </summary>
/// <returns></returns>
protected virtual async Task ApplyResponseCoreAsync()
{
await ApplyResponseGrantAsync();
await ApplyResponseChallengeAsync();
}
protected abstract void ApplyResponseGrant();
/// <summary>
/// Override this method to dela with sign-in/sign-out concerns, if an authentication scheme in question
/// deals with grant/revoke as part of it's request flow. (like setting/deleting cookies)
/// </summary>
/// <returns></returns>
protected virtual Task ApplyResponseGrantAsync()
{
ApplyResponseGrant();
return Task.FromResult(0);
}
protected abstract void ApplyResponseChallenge();
/// <summary>
/// Override this method to deal with 401 challenge concerns, if an authentication scheme in question
/// deals an authentication interaction as part of it's request flow. (like adding a response header, or
/// changing the 401 result to 302 of a login page or external sign-in location.)
/// 401 是 未授权 302 是重定向
/// </summary>
/// <returns></returns>
protected virtual Task ApplyResponseChallengeAsync()
{
ApplyResponseChallenge();
return Task.FromResult(0);
}
/// <summary>
/// OAuthAuthenticationHandler 里会调用此方法
/// </summary>
/// <param name="properties"></param>
protected void GenerateCorrelationId([NotNull] AuthenticationProperties properties)
{
string correlationKey = Constants.CorrelationPrefix + BaseOptions.AuthenticationType;
var nonceBytes = new byte[32];
CryptoRandom.GetBytes(nonceBytes);
string correlationId = TextEncodings.Base64Url.Encode(nonceBytes);
var cookieOptions = new CookieOptions
{
HttpOnly = true,
Secure = Request.IsSecure
};
// 比如: .AspNet.Correlation.Cookies
properties.Dictionary[correlationKey] = correlationId;
Response.Cookies.Append(correlationKey, correlationId, cookieOptions);
}
protected bool ValidateCorrelationId([NotNull] AuthenticationProperties properties, [NotNull] ILogger logger)
{
string correlationKey = Constants.CorrelationPrefix + BaseOptions.AuthenticationType;
string correlationCookie = Request.Cookies[correlationKey];
if (string.IsNullOrWhiteSpace(correlationCookie))
{
logger.WriteWarning("{0} cookie not found.", correlationKey);
return false;
}
var cookieOptions = new CookieOptions
{
HttpOnly = true,
Secure = Request.IsSecure
};
Response.Cookies.Delete(correlationKey, cookieOptions);
string correlationExtra;
if (!properties.Dictionary.TryGetValue(
correlationKey,
out correlationExtra))
{
logger.WriteWarning("{0} state property not found.", correlationKey);
return false;
}
properties.Dictionary.Remove(correlationKey);
if (!string.Equals(correlationCookie, correlationExtra, StringComparison.Ordinal))
{
logger.WriteWarning("{0} correlation cookie and state property mismatch.", correlationKey);
return false;
}
return true;
}
private void UnregisterAuthenticationHandler()
{
var auth = Context.GetAuthentication();
auth.Handler = PriorHandler;
}
}
浙公网安备 33010602011771号