11-005 Security 之 CookieAuthenticationHandler
Posted on 2015-04-22 17:45 DotNet1010 阅读(403) 评论(0) 收藏 举报--
internal class CookieAuthenticationHandler : AuthenticationHandler<CookieAuthenticationOptions>
{
private const string HeaderNameCacheControl = "Cache-Control";
// 预处理 指示
private const string HeaderNamePragma = "Pragma";
private const string HeaderNameExpires = "Expires";
private const string HeaderValueNoCache = "no-cache";
private const string HeaderValueMinusOne = "-1";
private const string SessionIdClaim = "Microsoft.AspNet.Security.Cookies-SessionId";
private readonly ILogger _logger;
private bool _shouldRenew;
private DateTimeOffset _renewIssuedUtc;
private DateTimeOffset _renewExpiresUtc;
private string _sessionKey;
public CookieAuthenticationHandler([NotNull] ILogger logger)
{
_logger = logger;
}
protected override AuthenticationTicket AuthenticateCore()
{
return AuthenticateCoreAsync().GetAwaiter().GetResult();
}
/// <summary>
/// 只有在ActiveMode 下才会执行 主要是修改 User 添加Claim
/// ClaimsIdentity identity = new ClaimsIdentity(new[] { new Claim(SessionIdClaim, _sessionKey) }, Options.AuthenticationType);
/// SessionIdClaim="Microsoft.AspNet.Security.Cookies-SessionId";
/// _sessionKey="";AuthenticationType="Cookies";
/// </summary>
/// <returns></returns>
protected override async Task<AuthenticationTicket> AuthenticateCoreAsync()
{
AuthenticationTicket ticket = null;
try
{
string cookie = Options.CookieManager.GetRequestCookie(Context, Options.CookieName);
if (string.IsNullOrWhiteSpace(cookie))
{
return null;
}
// 从Cookie 获取
ticket = Options.TicketDataFormat.Unprotect(cookie);
if (ticket == null)
{
_logger.WriteWarning(@"Unprotect ticket failed");
return null;
}
// 从Session获取
if (Options.SessionStore != null)
{
#region ---------
Claim claim = ticket.Identity.Claims.FirstOrDefault(c => c.Type.Equals(SessionIdClaim));
if (claim == null)
{
_logger.WriteWarning(@"SessionId missing");
return null;
}
_sessionKey = claim.Value;
ticket = await Options.SessionStore.RetrieveAsync(_sessionKey);
if (ticket == null)
{
_logger.WriteWarning(@"Identity missing in session store");
return null;
}
#endregion
}
DateTimeOffset currentUtc = Options.SystemClock.UtcNow;
DateTimeOffset? issuedUtc = ticket.Properties.IssuedUtc;
DateTimeOffset? expiresUtc = ticket.Properties.ExpiresUtc;
// 过期的话 直接退出
if (expiresUtc != null && expiresUtc.Value < currentUtc)
{
if (Options.SessionStore != null)
{
await Options.SessionStore.RemoveAsync(_sessionKey);
}
return null;
}
bool allowRefresh = ticket.Properties.AllowRefresh ?? true;
if (issuedUtc != null && expiresUtc != null && Options.SlidingExpiration && allowRefresh)
{
#region Refresh-----
TimeSpan timeElapsed = currentUtc.Subtract(issuedUtc.Value);
TimeSpan timeRemaining = expiresUtc.Value.Subtract(currentUtc);
// 剩余时间 小于 已经用于的时间
if (timeRemaining < timeElapsed)
{
_shouldRenew = true;
_renewIssuedUtc = currentUtc;
TimeSpan timeSpan = expiresUtc.Value.Subtract(issuedUtc.Value);
_renewExpiresUtc = currentUtc.Add(timeSpan);
}
#endregion
}
var context = new CookieValidateIdentityContext(Context, ticket, Options);
await Options.Notifications.ValidateIdentity(context);
// 从Cookie 获取的信息
return new AuthenticationTicket(context.Identity, context.Properties);
}
catch (Exception exception)
{
CookieExceptionContext exceptionContext = new CookieExceptionContext(Context, Options,
CookieExceptionContext.ExceptionLocation.Authenticate, exception, ticket);
Options.Notifications.Exception(exceptionContext);
if (exceptionContext.Rethrow)
{
throw;
}
return exceptionContext.Ticket;
}
}
protected override void ApplyResponseGrant()
{
ApplyResponseGrantAsync().GetAwaiter().GetResult();
}
/// <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)
/// 授予 撤销 登入 登出 设置Cookie 删除Cookie
/// </summary>
/// <returns></returns>
protected override async Task ApplyResponseGrantAsync()
{
var signin = SignInIdentityContext;
bool shouldSignin = signin != null; //执行过SignIn方法
var signout = SignOutContext;
bool shouldSignout = signout != null; //执行过SignOut方法
//全部是False 不登入 不登出 不ReNew 直接退出
if (!(shouldSignin || shouldSignout || _shouldRenew))
{
return;
}
//从Cookie 获取的信息
AuthenticationTicket model = await AuthenticateAsync();
try
{
var cookieOptions = new CookieOptions
{
Domain = Options.CookieDomain,
HttpOnly = Options.CookieHttpOnly,
Path = Options.CookiePath ?? (RequestPathBase.HasValue ? RequestPathBase.ToString() : "/"),
};
if (Options.CookieSecure == CookieSecureOption.SameAsRequest)
{
cookieOptions.Secure = Request.IsSecure;
}
else
{
cookieOptions.Secure = Options.CookieSecure == CookieSecureOption.Always;
}
if (shouldSignin)
{
#region ----shouldSignin----
var signInContext = new CookieResponseSignInContext(
Context,
Options,
Options.AuthenticationType,
signin.Identity,
signin.Properties,
cookieOptions);
DateTimeOffset issuedUtc;
if (signin.Properties.IssuedUtc.HasValue)
{
issuedUtc = signin.Properties.IssuedUtc.Value;
}
else
{
issuedUtc = Options.SystemClock.UtcNow;
signin.Properties.IssuedUtc = issuedUtc;
}
if (!signin.Properties.ExpiresUtc.HasValue)
{
signin.Properties.ExpiresUtc = issuedUtc.Add(Options.ExpireTimeSpan);
}
Options.Notifications.ResponseSignIn(signInContext);
if (signInContext.Properties.IsPersistent)
{
DateTimeOffset expiresUtc = signInContext.Properties.ExpiresUtc ?? issuedUtc.Add(Options.ExpireTimeSpan);
signInContext.CookieOptions.Expires = expiresUtc.ToUniversalTime().DateTime;
}
model = new AuthenticationTicket(signInContext.Identity, signInContext.Properties);
if (Options.SessionStore != null)
{
if (_sessionKey != null)
{
await Options.SessionStore.RemoveAsync(_sessionKey);
}
// 存储
_sessionKey = await Options.SessionStore.StoreAsync(model);
ClaimsIdentity identity = new ClaimsIdentity(
new[] { new Claim(SessionIdClaim, _sessionKey) },
Options.AuthenticationType);
model = new AuthenticationTicket(identity, null);
}
string cookieValue = Options.TicketDataFormat.Protect(model);
// 设置新的Cookie
Options.CookieManager.AppendResponseCookie(
Context,
Options.CookieName,
cookieValue,
signInContext.CookieOptions);
var signedInContext = new CookieResponseSignedInContext(
Context,
Options,
Options.AuthenticationType,
signInContext.Identity,
signInContext.Properties);
Options.Notifications.ResponseSignedIn(signedInContext);
#endregion
}
else if (shouldSignout)
{
#region----shouldSignout----
if (Options.SessionStore != null && _sessionKey != null)
{
await Options.SessionStore.RemoveAsync(_sessionKey);
}
var context = new CookieResponseSignOutContext(
Context,
Options,
cookieOptions);
Options.Notifications.ResponseSignOut(context);
//删除Cookie
Options.CookieManager.DeleteCookie(
Context,
Options.CookieName,
context.CookieOptions);
#endregion
}
else if (_shouldRenew)
{
#region ---ShouldReNew----
model.Properties.IssuedUtc = _renewIssuedUtc;
model.Properties.ExpiresUtc = _renewExpiresUtc;
if (Options.SessionStore != null && _sessionKey != null)
{
await Options.SessionStore.RenewAsync(_sessionKey, model);
ClaimsIdentity identity = new ClaimsIdentity(
new[] { new Claim(SessionIdClaim, _sessionKey) },
Options.AuthenticationType);
model = new AuthenticationTicket(identity, null);
}
string cookieValue = Options.TicketDataFormat.Protect(model);
if (model.Properties.IsPersistent)
{
cookieOptions.Expires = _renewExpiresUtc.ToUniversalTime().DateTime;
}
Options.CookieManager.AppendResponseCookie(
Context,
Options.CookieName,
cookieValue,
cookieOptions);
#endregion
}
Response.Headers.Set( HeaderNameCacheControl, HeaderValueNoCache);
Response.Headers.Set( HeaderNamePragma, HeaderValueNoCache);
Response.Headers.Set( HeaderNameExpires, HeaderValueMinusOne);
bool shouldLoginRedirect = shouldSignin && Options.LoginPath.HasValue && Request.Path == Options.LoginPath;
bool shouldLogoutRedirect = shouldSignout && Options.LogoutPath.HasValue && Request.Path == Options.LogoutPath;
if ((shouldLoginRedirect || shouldLogoutRedirect) && Response.StatusCode == 200)
{
IReadableStringCollection query = Request.Query;
string redirectUri = query.Get(Options.ReturnUrlParameter);
if (!string.IsNullOrWhiteSpace(redirectUri)
&& IsHostRelative(redirectUri))
{
var redirectContext = new CookieApplyRedirectContext(Context, Options, redirectUri);
// 重定向 ajax 或者 直接重定向
Options.Notifications.ApplyRedirect(redirectContext);
}
}
}
catch (Exception exception)
{
CookieExceptionContext exceptionContext = new CookieExceptionContext(Context, Options,
CookieExceptionContext.ExceptionLocation.ApplyResponseGrant, exception, model);
Options.Notifications.Exception(exceptionContext);
if (exceptionContext.Rethrow)
{
throw;
}
}
}
protected override void ApplyResponseChallenge()
{
//不是 401 状态码 或者 没有LoginPath 直接退出
if (Response.StatusCode != 401 || !Options.LoginPath.HasValue )
{
return;
}
// 是401 并且有LoginPath 继续执行
// Active middleware should redirect on 401 even if there wasn't an explicit challenge.
if (ChallengeContext == null && Options.AuthenticationMode == AuthenticationMode.Passive)
{
return;
}
string loginUri = string.Empty;
if (ChallengeContext != null)
{
// return Dictionary.TryGetValue(".redirect", out value) ? value : null;
loginUri = new AuthenticationProperties(ChallengeContext.Properties).RedirectUri;
}
try
{
if (string.IsNullOrWhiteSpace(loginUri))
{
#region -- set currentUri ; loginUri
string currentUri =
Request.PathBase +
Request.Path +
Request.QueryString;
loginUri =
Request.Scheme +
"://" +
Request.Host +
Request.PathBase +
Options.LoginPath +
new QueryString(Options.ReturnUrlParameter, currentUri);
#endregion
}
var redirectContext = new CookieApplyRedirectContext(Context, Options, loginUri);
// ajax请求 返回成功 和 信息 其它直接重定向
Options.Notifications.ApplyRedirect(redirectContext);
}
catch (Exception exception)
{
CookieExceptionContext exceptionContext = new CookieExceptionContext(Context, Options,
CookieExceptionContext.ExceptionLocation.ApplyResponseChallenge, exception, ticket: null);
Options.Notifications.Exception(exceptionContext);
if (exceptionContext.Rethrow)
{
throw;
}
}
}
private static bool IsHostRelative(string path)
{
if (string.IsNullOrEmpty(path))
{
return false;
}
if (path.Length == 1)
{
return path[0] == '/';
}
return path[0] == '/' && path[1] != '/' && path[1] != '\\';
}
}
浙公网安备 33010602011771号