Azure AD 身份验证成功后 HttpContext.Current 为空
HttpContext.Current is null after Azure AD authentication success
问题:是什么原因导致 HttpContext.Current
仅有时为空?
问题:调用PrincipalService.OnAzureAuthenticationSuccess
后是否初始化了HttpContext.Current
?如果是这样,为什么只有一些时间?
描述
经常发生的情况是,用户点击登录后 HttpContext.Current
将为空,导致永远不会设置 cookie。这会将他们重定向回主页,并且由于未设置 cookie,他们一次又一次地单击登录。有时它会决定在点击 2 或 3 次后设置 cookie,其他时候如果不清除 cookie 或注销另一个 Azure AD 帐户(例如,我们的共享点服务器使用 Azure AD),它不会这样做。
这些事情对我来说似乎很奇怪,尽管我已经进行了数小时的研究,但我仍无法确定原因。
Azure 配置
public static void ConfigureAzure(IAppBuilder app)
{
// COOKIES: Tells it to use cookies for authentication.
app.SetDefaultSignInAsAuthenticationType(CookieAuthenticationDefaults.AuthenticationType);
app.UseCookieAuthentication(new CookieAuthenticationOptions()
{
// CUSTOMIZE: This is where you would adjust cookie experiation and things of that nature.
SlidingExpiration = true,
ExpireTimeSpan = TimeSpan.FromHours(CookieDurationInHours)
});
//https://azure.microsoft.com/en-us/resources/samples/active-directory-dotnet-webapp-webapi-openidconnect/
// OPEN-ID: Handle OpenID stuff.
var notifications = new OpenIdConnectAuthenticationNotifications()
{
AuthenticationFailed = PrincipalService.OnAzureAuthenticationFailure,
// REFERENCE: https://russellyoung.net/2015/09/05/mvc-role-based-authorization-with-azure-active-directory-aad/
AuthorizationCodeReceived = PrincipalService.OnAzureAuthenticationSuccess
};
var options = new OpenIdConnectAuthenticationOptions()
{
ClientId = ClientID,
Authority = Authority,
PostLogoutRedirectUri = PostLogoutRedirectUri,
Notifications = notifications
};
app.UseOpenIdConnectAuthentication(options);
}
在 Azure 上取得成功
/// <summary>
/// Stores the proper identity cookie (doesn't have customer permissions yet).
/// </summary>
public static Task OnAzureAuthenticationSuccess(AuthorizationCodeReceivedNotification context)
{
var success = false;
var username = context.AuthenticationTicket.Identity.Name;
try
{
success = StoreCookie(username);
}
catch (DbEntityValidationException ex)
{
var errors = ex.EntityValidationErrors.FirstOrDefault()?.ValidationErrors.FirstOrDefault()?.ErrorMessage;
Logger.Log(Level.Error, "An error occurred while storing authentication cookie.", ex);
return Task.FromResult(0);
}
catch (Exception ex)
{
Logger.Log(Level.Error, "An error occurred while storing authentication cookie.", ex);
return Task.FromResult(0);
}
if (success)
{
Logger.Log(Level.Cookie, "Login Complete. Cookie stored successfully. Username: '" + username + "'.");
}
return Task.FromResult(0);
}
存储 Cookie
/// <summary>
/// Creates and stores a forms authentication cookie for the user.
/// </summary>
private static bool StoreCookie(string username, bool rememberMe = false)
{
var azureUsers = new AzureUserRepository(new AuthenticationEntities());
var user = azureUsers.Get(u => u.Username == username);
if (user == null)
{
Logger.Log(Level.Cookie, "User '" + username + "' not found.");
throw new NullReferenceException();
}
// Clear any old existing cookies.
if (HttpContext.Current == null)
{
// HERE: This is where it is null (again, only sometimes).
Logger.Log(Level.Debug, "HttpContext is null.");
return false;
}
if (HttpContext.Current.Request == null)
{
Logger.Log(Level.Debug, "HttpContext.Current.Request is null.");
return false;
}
if (HttpContext.Current == null && HttpContext.Current.Response != null)
{
Logger.Log(Level.Debug, "HttpContext.Current.Response is null.");
return false;
}
HttpContext.Current.Request.RemoveFormsAuthCookie();
HttpContext.Current.Response.RemoveFormsAuthCookie();
// Create the principal from the user object.
var principal = new PrincipalModel(user);
// Create and store the cookie in the response.
HttpContext.Current.Response.AddFormsAuthCookie(
username: user.Username,
userData: principal.SerializeUserData(),
isPersistent: true
);
return true;
}
账户控制器
[AllowAnonymous]
public void SignIn()
{
if (Request.IsAuthenticated) { return; }
HttpContext.GetOwinContext().Authentication.Challenge(
new AuthenticationProperties() { RedirectUri = "/" }, OpenIdConnectAuthenticationDefaults.AuthenticationType
);
Logger.Log(Level.Info, "Sign-In clicked.");
}
public void SignOut()
{
if (!Request.IsAuthenticated) { return; }
// SIGN OUT:
HttpContext.GetOwinContext().Authentication.SignOut(
OpenIdConnectAuthenticationDefaults.AuthenticationType, CookieAuthenticationDefaults.AuthenticationType
);
Logger.Log(Level.Info, "Sign-out clicked.");
// COOKIE: Remove the cookie.
var cookie = Request.Cookies[FormsAuthentication.FormsCookieName];
cookie.Expires = DateTime.Now.AddDays(-1); // DateTime.UtcNow.AddDays(-1);
Response.Cookies.Add(cookie);
}
好吧,事实证明我是个大笨蛋,做事很艰难。我不是 100% 完全理解为什么它是空的,但我确实找到了解决这个问题的更简单的方法。
- 首先删除与我创建自己的 cookie 相关的代码(即
AuthorizationCodeReceived = PrincipalService.OnAzureAuthenticationSuccess
)。
发生的事情是我意识到 Azure AD 正在通过 app.UseCookieAuthentication(new CookieAuthenticationOptions());
创建自己的主体和 cookie 这种实现要归功于 Fei Xue 链接的 git hub 项目。
- 在那之后,我将我的自定义主体切换为基于 'built-in' cookie 而不是我正在创建的 cookie(由于
HttpContext.Current
是 null
).
既然自定义委托人的创建不依赖于 HttpContext.Current
我根本没有发生登录循环,因为委托人和 cookie 都存在。
非常感谢飞雪!
问题:是什么原因导致 HttpContext.Current
仅有时为空?
问题:调用PrincipalService.OnAzureAuthenticationSuccess
后是否初始化了HttpContext.Current
?如果是这样,为什么只有一些时间?
描述
经常发生的情况是,用户点击登录后 HttpContext.Current
将为空,导致永远不会设置 cookie。这会将他们重定向回主页,并且由于未设置 cookie,他们一次又一次地单击登录。有时它会决定在点击 2 或 3 次后设置 cookie,其他时候如果不清除 cookie 或注销另一个 Azure AD 帐户(例如,我们的共享点服务器使用 Azure AD),它不会这样做。
这些事情对我来说似乎很奇怪,尽管我已经进行了数小时的研究,但我仍无法确定原因。
Azure 配置
public static void ConfigureAzure(IAppBuilder app)
{
// COOKIES: Tells it to use cookies for authentication.
app.SetDefaultSignInAsAuthenticationType(CookieAuthenticationDefaults.AuthenticationType);
app.UseCookieAuthentication(new CookieAuthenticationOptions()
{
// CUSTOMIZE: This is where you would adjust cookie experiation and things of that nature.
SlidingExpiration = true,
ExpireTimeSpan = TimeSpan.FromHours(CookieDurationInHours)
});
//https://azure.microsoft.com/en-us/resources/samples/active-directory-dotnet-webapp-webapi-openidconnect/
// OPEN-ID: Handle OpenID stuff.
var notifications = new OpenIdConnectAuthenticationNotifications()
{
AuthenticationFailed = PrincipalService.OnAzureAuthenticationFailure,
// REFERENCE: https://russellyoung.net/2015/09/05/mvc-role-based-authorization-with-azure-active-directory-aad/
AuthorizationCodeReceived = PrincipalService.OnAzureAuthenticationSuccess
};
var options = new OpenIdConnectAuthenticationOptions()
{
ClientId = ClientID,
Authority = Authority,
PostLogoutRedirectUri = PostLogoutRedirectUri,
Notifications = notifications
};
app.UseOpenIdConnectAuthentication(options);
}
在 Azure 上取得成功
/// <summary>
/// Stores the proper identity cookie (doesn't have customer permissions yet).
/// </summary>
public static Task OnAzureAuthenticationSuccess(AuthorizationCodeReceivedNotification context)
{
var success = false;
var username = context.AuthenticationTicket.Identity.Name;
try
{
success = StoreCookie(username);
}
catch (DbEntityValidationException ex)
{
var errors = ex.EntityValidationErrors.FirstOrDefault()?.ValidationErrors.FirstOrDefault()?.ErrorMessage;
Logger.Log(Level.Error, "An error occurred while storing authentication cookie.", ex);
return Task.FromResult(0);
}
catch (Exception ex)
{
Logger.Log(Level.Error, "An error occurred while storing authentication cookie.", ex);
return Task.FromResult(0);
}
if (success)
{
Logger.Log(Level.Cookie, "Login Complete. Cookie stored successfully. Username: '" + username + "'.");
}
return Task.FromResult(0);
}
存储 Cookie
/// <summary>
/// Creates and stores a forms authentication cookie for the user.
/// </summary>
private static bool StoreCookie(string username, bool rememberMe = false)
{
var azureUsers = new AzureUserRepository(new AuthenticationEntities());
var user = azureUsers.Get(u => u.Username == username);
if (user == null)
{
Logger.Log(Level.Cookie, "User '" + username + "' not found.");
throw new NullReferenceException();
}
// Clear any old existing cookies.
if (HttpContext.Current == null)
{
// HERE: This is where it is null (again, only sometimes).
Logger.Log(Level.Debug, "HttpContext is null.");
return false;
}
if (HttpContext.Current.Request == null)
{
Logger.Log(Level.Debug, "HttpContext.Current.Request is null.");
return false;
}
if (HttpContext.Current == null && HttpContext.Current.Response != null)
{
Logger.Log(Level.Debug, "HttpContext.Current.Response is null.");
return false;
}
HttpContext.Current.Request.RemoveFormsAuthCookie();
HttpContext.Current.Response.RemoveFormsAuthCookie();
// Create the principal from the user object.
var principal = new PrincipalModel(user);
// Create and store the cookie in the response.
HttpContext.Current.Response.AddFormsAuthCookie(
username: user.Username,
userData: principal.SerializeUserData(),
isPersistent: true
);
return true;
}
账户控制器
[AllowAnonymous]
public void SignIn()
{
if (Request.IsAuthenticated) { return; }
HttpContext.GetOwinContext().Authentication.Challenge(
new AuthenticationProperties() { RedirectUri = "/" }, OpenIdConnectAuthenticationDefaults.AuthenticationType
);
Logger.Log(Level.Info, "Sign-In clicked.");
}
public void SignOut()
{
if (!Request.IsAuthenticated) { return; }
// SIGN OUT:
HttpContext.GetOwinContext().Authentication.SignOut(
OpenIdConnectAuthenticationDefaults.AuthenticationType, CookieAuthenticationDefaults.AuthenticationType
);
Logger.Log(Level.Info, "Sign-out clicked.");
// COOKIE: Remove the cookie.
var cookie = Request.Cookies[FormsAuthentication.FormsCookieName];
cookie.Expires = DateTime.Now.AddDays(-1); // DateTime.UtcNow.AddDays(-1);
Response.Cookies.Add(cookie);
}
好吧,事实证明我是个大笨蛋,做事很艰难。我不是 100% 完全理解为什么它是空的,但我确实找到了解决这个问题的更简单的方法。
- 首先删除与我创建自己的 cookie 相关的代码(即
AuthorizationCodeReceived = PrincipalService.OnAzureAuthenticationSuccess
)。
发生的事情是我意识到 Azure AD 正在通过 app.UseCookieAuthentication(new CookieAuthenticationOptions());
创建自己的主体和 cookie 这种实现要归功于 Fei Xue 链接的 git hub 项目。
- 在那之后,我将我的自定义主体切换为基于 'built-in' cookie 而不是我正在创建的 cookie(由于
HttpContext.Current
是null
).
既然自定义委托人的创建不依赖于 HttpContext.Current
我根本没有发生登录循环,因为委托人和 cookie 都存在。
非常感谢飞雪!