【问题标题】:HttpContext.Current is null after Azure AD authentication successAzure AD 身份验证成功后,HttpContext.Current 为 null
【发布时间】:2017-09-29 05:21:04
【问题描述】:

问题:什么可能导致 HttpContext.Current 有时为空?

问题:HttpContext.Current是在调用PrincipalService.OnAzureAuthenticationSuccess之后初始化的吗?如果是这样,为什么只有某些时候?

说明

经常发生的情况是用户点击登录,HttpContext.Current 将为空,导致 cookie 永远不会被设置。这会将他们重定向回主页,并且由于未设置 cookie,因此他们一次又一次地单击登录。有时它会决定让 cookie 在单击 2 或 3 次后设置,有时它不会在不清除 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);
}

【问题讨论】:

  • 既然你提到这个问题每次都无法重现。怀疑它可能是由代码引起的。为了缩小这个问题的范围,我建议您删除除与身份验证相关的代码之外的所有代码。我正在使用示例here 测试这个问题。并添加自定义代码,如AuthorizationCodeReceived = (notification) =&gt; {var a = HttpContext.Current;return Task.FromResult(0);}
  • 我无法重现您的问题。我有两个使用 Azure AD 身份验证的应用程序。我多次通过AuthorizationCodeReceived 处理程序,在所有情况下HttpContext.Current 都不为空。

标签: c# azure-active-directory openid-connect httpcontext


【解决方案1】:

好吧,事实证明我是个大傻瓜,做事很艰难。我不是 100% 完全理解为什么它为空,但我确实找到了解决这个问题的更简单的方法。

  1. 首先删除与我创建自己的 cookie 相关的代码(即AuthorizationCodeReceived = PrincipalService.OnAzureAuthenticationSuccess)。

我意识到 Azure AD 正在通过 app.UseCookieAuthentication(new CookieAuthenticationOptions()); 创建自己的主体和 cookie,这要归功于飞雪链接的 git hub 项目。

  1. 之后,我将自定义主体切换为基于“内置”cookie 而不是我正在创建的 cookie(由于 HttpContext.Currentnull 而创建的时间只有一半)。

现在自定义主体创建不依赖于HttpContext.Current,我根本没有发生登录循环,因为主体和 cookie 都存在。

非常感谢飞雪!

【讨论】:

  • 您好,您是否通过删除自定义 cookie 创建器解决了这个问题?
猜你喜欢
  • 2019-06-25
  • 1970-01-01
  • 1970-01-01
  • 2020-07-12
  • 2019-09-02
  • 2021-11-26
  • 2019-10-15
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多