Почему ServiceConfiguration.GetCurrent может возвращать нулевую ссылку на объект после выхода / входа?

У меня есть приложение Windows Azure, состоящее из веб-роли MVC4 и набора служебных ролей, как рабочих, так и веб-. Все они созданы с использованием SDK, выпущенного в июне 2012 года. Моя роль MVC защищена с помощью WIF/ACS и пассивного перенаправления. Мои услуги доступны только через внутренние конечные точки и не защищены.

Аутентификация и авторизация с помощью WIF/ACS в целом работает нормально, но иногда, если я выхожу из одной учетной записи, а затем быстро, из истории браузера, возвращаюсь на страницу индекса, ожидая, что меня попросят войти снова, вместо этого мне предлагают следующий YSOD:

 Object reference not set to an instance of an object.
 Description: An unhandled exception occurred during the execution of the current web request. Please review the stack trace for more information about the error and where it originated in the code.

 Exception Details: System.NullReferenceException: Object reference not set to an instance of an object.

 Source Error:

 An unhandled exception was generated during the execution of the current web request. Information regarding the origin and location of the exception can be identified using the exception stack trace below.

 Stack Trace:

 [NullReferenceException: Object reference not set to an instance of an object.]
    Microsoft.IdentityModel.Configuration.ServiceConfiguration.GetCurrent() +123
    Microsoft.IdentityModel.Claims.ClaimsPrincipal.CreateFromHttpContext(HttpContext httpContext, Boolean clientCertificateAuthenticationEnabled) +29
    Microsoft.IdentityModel.Web.WSFederationAuthenticationModule.OnPostAuthenticateRequest(Object sender, EventArgs e) +91
    System.Web.SyncEventExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute() +79
    System.Web.HttpApplication.ExecuteStep(IExecutionStep step, Boolean& completedSynchronously) +269

Сначала я подумал, что это может быть случай, когда куки не удаляются, но на удивление кажется, что весь сайт MVC потерпел крах, и эта ошибка видна из любого браузера и с других компьютеров. Единственное исправление, которое я нашел, - перезапустить роль.

Так как пассивное перенаправление настроено в web.config и применяется автоматически с помощью мастера STS, я подозреваю, что я делаю что-то не так, когда я выхожу из системы. Мой код выхода из системы выглядит так:

public ActionResult CompleteLogOut(Boolean total, String provider = "")
    {
        WSFederationAuthenticationModule fam = FederatedAuthentication.WSFederationAuthenticationModule;
        FormsAuthentication.SignOut();
        fam.SignOut(true);
        if (total)
        {
            provider = @"logout:" + provider;
            return Redirect(ConfigurationManager.AppSettings[provider]);
        }
        else
        {
            return RedirectToAction("Index");
        }
    }

... где, если 'total' равно true, пользователь перенаправляется на страницу выхода для своего провайдера идентификации: https://accounts.google.com/Logout или https://login.live.com/login.srf?wa=wsignout1.0 в моей тестовой настройке.

Я только заметил, что ошибка происходит, когда мой выход из системы "тотален", но, поскольку в любом случае это временная ошибка, я не могу быть уверен, что это всегда так.

При поиске ошибки возникает вопрос, который не кажется актуальным, но заставляет задуматься, нужно ли мне удалять модуль WSFederationAuthenticationModule после выхода из системы?

Кто-нибудь сталкивался с подобной проблемой, или у кого-нибудь есть идея, что это может быть за проблема?

ОБНОВЛЕНИЕ:

"кажется, что весь сайт MVC потерпел крах, и эта ошибка видна из любого браузера" - я не думаю, что это действительно так. Только два отдельных пользователя испытывают одну и ту же ошибку, по совпадению в одно и то же время.

1 ответ

Решение

Хм, ну, я нашел ответ на это, но я не до конца понимаю.

Чтобы отслеживать пользователей, я устанавливаю переменную сеанса в Global.asax, как только кто-то начинает новый сеанс:

    protected void Session_Start()
    {
        HttpContext.Current.Session.Add("MySession", Session.SessionID);
    }

При этом MVC создает сеанс, поэтому я могу отслеживать активность пользователя с помощью SessionID (в противном случае MVC создаст новый сеанс для каждого запроса).

Если я удаляю свой сеанс явно при выходе из системы, то вышеуказанная ошибка исчезает:

    public ActionResult CompleteLogOut(Boolean total, String provider = "")
    {
        var fam = FederatedAuthentication.WSFederationAuthenticationModule;
        Session.Clear();
        Session.Abandon();
        FormsAuthentication.SignOut();
        try
        {
            fam.SignOut(true);
        }
        catch {}
        finally
        {
            try
            {
                fam.Dispose();
                fam = null;
            }
            catch { }
        }
        if (total)
        {
            provider = @"logout:" + provider;
            return Redirect(ConfigurationManager.AppSettings[provider]);
        }
        else
        {
            return Redirect(ConfigurationManager.AppSettings["LoggedOutRedirect"]);
        }
    }
}

И если пользователь не входит в систему полностью из своего провайдера идентификации, мне также нужно перенаправить на абсолютный URI моей домашней страницы, а не просто перенаправить на метод действия.

Не уверен, почему сеанс должен быть очищен и оставлен, чтобы WIF снова работал. WIF использует сессию внутри? Возможно, кто-то может пролить больше света, но в любом случае вышеприведенная процедура выхода из строя работает и, надеюсь, может спасти кого-то еще от борьбы с той же ошибкой в ​​будущем.

Другие вопросы по тегам