Оказывается, проблема заключалась в том, что ClaimsPrincipal поддерживал несколько идентификаторов. Если вы находитесь в ситуации, когда у вас есть несколько идентификаторов, он выбирает один самостоятельно. Я не знаю, что определяет порядок тождеств в IEnumerable, но каким бы он ни был, он, очевидно, обязательно приводит к постоянному порядку в течение жизненного цикла сеанса пользователя.
Как уже упоминалось в разделе «Проблемы» gp asp.net/Security, Проверка подлинности NTLM и cookie # 1467 :
Идентификационные данные содержат как идентификатор Windows, так и идентификационный файл cookie.
и
Похоже, что с ClaimsPrincipals
вы можете установить static Func<IEnumerable<ClaimsIdentity>, ClaimsIdentity>
с именем PrimaryIdentitySelector
, который можно использовать для выбора основного идентификатора для работы.
Для этого создайте статический метод с подписью:
static ClaimsIdentity MyPrimaryIdentitySelectorFunc(IEnumerable<ClaimsIdentity> identities)
Этот метод будет использоваться для просмотра списка ClaimsIdentity
s и выбора того, который вы предпочитаете.
Затем в вашем Global.asax.cs установите этот метод как PrimaryIdentitySelector
, например так:
System.Security.Claims.ClaimsPrincipal.PrimaryIdentitySelector = MyPrimaryIdentitySelectorFunc;
Мой PrimaryIdentitySelector
метод в итоге выглядел так:
public static ClaimsIdentity PrimaryIdentitySelector(IEnumerable<ClaimsIdentity> identities)
{
//check for null (the default PIS also does this)
if (identities == null) throw new ArgumentNullException(nameof(identities));
//if there is only one, there is no need to check further
if (identities.Count() == 1) return identities.First();
//Prefer my cookie identity. I can recognize it by the IdentityProvider
//claim. This doesn't need to be a unique value, simply one that I know
//belongs to the cookie identity I created. AntiForgery will use this
//identity in the anti-CSRF check.
var primaryIdentity = identities.FirstOrDefault(identity => {
return identity.Claims.FirstOrDefault(c => {
return c.Type.Equals(StringConstants.ClaimTypes_IdentityProvider, StringComparison.Ordinal) &&
c.Value == StringConstants.Claim_IdentityProvider;
}) != null;
});
//if none found, default to the first identity
if (primaryIdentity == null) return identities.First();
return primaryIdentity;
}
[Изменить]
Теперь этого оказалось недостаточно, так как PrimaryIdentitySelector
, похоже, не работает, когда в списке Identities
есть только один Identity
. Это вызвало проблемы на странице входа в систему, когда браузер иногда передавал идентификатор WindowsIdentity при загрузке страницы, но не передавал его по запросу входа в систему {вздохнул вздох}. Чтобы решить это я закончил тем, что создал ClaimsIdentity для страницы входа в систему, затем вручную переписал принципала потока, как описано в этом SO вопросе .
Это создает проблему с проверкой подлинности Windows, так как OnAuthenticate
не отправит 401 для запроса идентификатора Windows. Чтобы решить это , вы должны выйти из учетной записи. Если вход в систему не удается, не забудьте воссоздать пользователя входа в систему. (Вам также может понадобиться воссоздать токен CSRF)