Thread.CurrentPrincipal претензий неправильно, чтобы быть anynomous
Я вижу запросы на моем сервере, которые, как представляется, сделаны кем-либо, хотя я уверен, что они были сделаны аутентифицированным пользователем - у меня есть журналы fiddler, показывающие, что клиент отправил действительные cookie файлы asp.net, и журналы сервера, указывающие, что cookie прибыл и действителен. Проблема наблюдается во всех браузерах.
Поток данных:
- Пользователь посещает URL-адрес входа в систему, на машине, которая является частью webfarm (все с той же машиной, расшифровывают ключи).
- При успешной аутентификации форм пользователь перенаправляется на ограниченный URL-адрес, на своей домашней странице
- Домашняя страница отображается правильно, известна идентификация пользователя и включает javascript для выполнения 7 асинхронных пост-обратных связей для получения дополнительных данных.
- Javascript запускает 7 запросов http.get, ударяя по разным машинам в webfarm (предположим, что вокруг robin)
- Сервер проверяет запрос: ~ 0.01% не удается выполнить проверку подлинности.
Мысли?
Теперь о деталях:
Очень небольшое число асинхронных запросов приземляется на моем сервере (с доказательствами, что они не были манипулированы или подделаны) и кажутся анонимными. Из 7 выполненных запросов некоторое число может работать или не работать (т.е. 5/7 удастся, 2 не удастся). Кажется, что в успехах/неудачах нет никаких шаблонов. В тех случаях, когда мои запросы кажутся анонимными, CurrentPrincipal.Identity записывает:
Thread.CurrentPrincipal.Identity.IsAuthenticated; // false
Thread.CurrentPrincipal.Identity.Name; // null (or empty, unsure off hand)
Отбрасывая коллекцию http.context.request.params в файл журнала, я могу видеть следующие релевантные (и очищенные) свойства (полные параметры ниже):
context: {"userId":10000,"userName":"johnsmith"}
HTTP_COOKIE:.ASPXAUTH=[valid auth cookie value]
HTTP_X_REQUESTED_WITH:XMLHttpRequest
X-Requested-With: XMLHttpRequest
AUTH_TYPE:
AUTH_USER:
AUTH_PASSWORD:
LOGON_USER:
REMOTE_USER:
HTTP_COOKIE: .ASPXAUTH=[valid auth cookie value]
Я знаю, что файл cookie auth действителен - во время этих же запросов я могу расшифровать файл cookie auth и извлечь следующее:
CookiePath: /
Expiration: 9/23/2105 8:14:22 PM
Expired: False
IsPersistent: True
IssueDate: 8/30/2010 2:54:22 PM
Name: johnsmith
UserData:
Version: 2
Не уверен, как это сделать. Эта проблема, похоже, усугубилась нашей недавней миграцией в mvc 2.0/asp.net 4.0, но моя уверенность не была высокой, что было причиной.
Я обратился к горстке моих клиентов, у которых была эта проблема, и это было еще более неприятно (но отражает то, что я могу читать в своих журналах). Когда-то в этом состоянии ему трудно выбраться. Очистка кеша и файлов cookie, похоже, не имеет никакого эффекта. Но, перейдя на новый браузер, и это вообще нормально. Аналогично, ожидая несколько часов и возвращаясь с тем же браузером, и это также нормально, но не всегда. Как было сказано ранее, это видно во всех браузерах.
Любые предложения?
Кевин
------------
Вот оставшаяся часть фрагмента журнала (очищенная для pii), которую я захватил:
8/30/2010 2:54:43 PM: Anonymous user detected:
Identity Name:
IsAuthenticated::False
HttpContextInformation to follow:
8/30/2010 2:54:43 PM: Request Param collection contents:
context: {"userId":10000,"userName":"johnsmith"}
.ASPXAUTH: A3C6615642F1F543397160C84C0E016C8439BDF400B0130AADAB82C93E77FFF3BEAD7726223F02049FA65B2C3E1773928C0371C4F580F2432C1538551BC5654020AD76F37159BA6BB68D7A68744AE036
ASP.NET_SessionId: m5vit3cyv0rsiosqg5xmhhuu
ALL_HTTP: HTTP_CONNECTION:close
HTTP_ACCEPT:text/javascript, text/html, application/xml, text/xml, */*
HTTP_ACCEPT_ENCODING:gzip, deflate
HTTP_ACCEPT_LANGUAGE:en-us
HTTP_COOKIE:.ASPXAUTH=A3C6615642F1F543397160C84C0E016C8439BDF400B0130AADAB82C93E77FFF3BEAD7726223F02049FA65B2C3E1773928C0371C4F580F2432C1538551BC5654020AD76F37159BA6BB68D7A68744AE036
HTTP_HOST:www.host.com
HTTP_REFERER:http://www.host.com/
HTTP_USER_AGENT:Mozilla/5.0 (iPhone; U; CPU iPhone OS 4_0 like Mac OS X; en-us) AppleWebKit/532.9 (KHTML, like Gecko) Version/4.0.5 Mobile/8A293 Safari/6531.22.7
HTTP_X_FORWARDED_FOR:166.137.139.139
HTTP_X_REQUESTED_WITH:XMLHttpRequest
ALL_RAW: Connection: close
Accept: text/javascript, text/html, application/xml, text/xml, */*
Accept-Encoding: gzip, deflate
Accept-Language: en-us
Cookie: .ASPXAUTH=A3C6615642F1F543397160C84C0E016C8439BDF400B0130AADAB82C93E77FFF3BEAD7726223F02049FA65B2C3E1773928C0371C4F580F2432C1538551BC5654020AD76F37159BA6BB68D7A68744AE036
Host: www.host.com
Referer: http://www.host.com/
User-Agent: Mozilla/5.0 (iPhone; U; CPU iPhone OS 4_0 like Mac OS X; en-us) AppleWebKit/532.9 (KHTML, like Gecko) Version/4.0.5 Mobile/8A293 Safari/6531.22.7
X-Forwarded-For: 166.137.139.139
X-Requested-With: XMLHttpRequest
APPL_MD_PATH: /LM/W3SVC/792523/Root
APPL_PHYSICAL_PATH: d:\localpath\
AUTH_TYPE:
AUTH_USER:
AUTH_PASSWORD:
LOGON_USER:
REMOTE_USER:
CERT_COOKIE:
CERT_FLAGS:
CERT_ISSUER:
CERT_KEYSIZE:
CERT_SECRETKEYSIZE:
CERT_SERIALNUMBER:
CERT_SERVER_ISSUER:
CERT_SERVER_SUBJECT:
CERT_SUBJECT:
CONTENT_LENGTH: 0
CONTENT_TYPE:
GATEWAY_INTERFACE: CGI/1.1
HTTPS: off
HTTPS_KEYSIZE:
HTTPS_SECRETKEYSIZE:
HTTPS_SERVER_ISSUER:
HTTPS_SERVER_SUBJECT:
INSTANCE_ID: 792523
INSTANCE_META_PATH: /LM/W3SVC/792523
LOCAL_ADDR: 10.248.50.207
PATH_INFO: /resource
PATH_TRANSLATED: d:\localpath\resource
QUERY_STRING: context={%22userId%22:10000,%22userName%22:%22johnsmith%22}
REMOTE_ADDR: 10.208.205.171
REMOTE_HOST: 10.208.205.171
REMOTE_PORT: 37966
REQUEST_METHOD: GET
SCRIPT_NAME: /resouce
SERVER_NAME: www.host.com
SERVER_PORT: 80
SERVER_PORT_SECURE: 0
SERVER_PROTOCOL: HTTP/1.0
SERVER_SOFTWARE: Microsoft-IIS/6.0
URL: /resource
HTTP_CONNECTION: close
HTTP_ACCEPT: text/javascript, text/html, application/xml, text/xml, */*
HTTP_ACCEPT_ENCODING: gzip, deflate
HTTP_ACCEPT_LANGUAGE: en-us
HTTP_COOKIE: .ASPXAUTH=A3C6615642F1F543397160C84C0E016C8439BDF400B0130AADAB82C93E77FFF3BEAD7726223F02049FA65B2C3E1773928C0371C4F580F2432C1538551BC5654020AD76F37159BA6BB68D7A68744AE036
HTTP_HOST: www.host.com
HTTP_REFERER: http://www.host.com/
HTTP_USER_AGENT: Mozilla/5.0 (iPhone; U; CPU iPhone OS 4_0 like Mac OS X; en-us) AppleWebKit/532.9 (KHTML, like Gecko) Version/4.0.5 Mobile/8A293 Safari/6531.22.7
HTTP_X_FORWARDED_FOR: 166.137.139.139
HTTP_X_REQUESTED_WITH: XMLHttpRequest
8/30/2010 2:54:43 PM: Auth Ticket collection contents:
CookiePath: /
Expiration: 9/23/2105 8:14:22 PM
Expired: False
IsPersistent: True
IssueDate: 8/30/2010 2:54:22 PM
Name: johnsmith
UserData:
Version: 2
Ответы
Ответ 1
Этот ответ работает, но одновременно вызывает разочарование и тревогу.
Я провел 2 месяца, работая неполный рабочий день с технической поддержкой MSDN, и мы, наконец, нашли работу по этой проблеме. Я собираюсь оставить его в Microsoft, чтобы исправить эту проблему, поскольку это почти наверняка проблема в .NET Framework. Но прежде всего позвольте мне подвести итог этой проблеме, предоставить дополнительную соответствующую справочную информацию и несколько интересных вещей, которые мы нашли на этом пути.
Симптом был мимолетный: на нашем веб-сайте было несколько сообщений о пользователях, которые будут аутентифицироваться на нашем веб-сайте и видеть правильное состояние. Но, по-видимому, случайным образом, они должны быть выписаны из нашего веб-сайта, или изредка, сталкиваются с анонимным представлением. Это было очень неприятно, почти невозможно воспроизвести локально, в доме.
Также по симптомам: через регистрацию мы видели, что на мобильные (iphone, ipad и android) браузеры были непропорционально затронуты, хотя все браузеры и операционные системы в какой-то степени были затронуты. И последний случайный симптом: Itd работает для пользователя один день, а не следующий, а позже днем - снова работать. Конечно, коммутационные браузеры почти всегда устраняли проблему.
Мы быстро взяли вопрос auth, вероятно, вокруг файла cookie аутентификации. Странная часть заключалась в том, что я смог доказать (отслеживание), что запросы, которые были ошибками, включали в себя правильный файл cookie, который я смог расшифровать, но что .net-инфраструктура игнорировалась недетерминированным способом. В конечном итоге я начал регистрировать все запросы, которые были в этом состоянии - auth cookie, проигнорированный инфраструктурой .net, но, по-видимому, действительный для меня, и нашлось около 2% моих пользователей.
Я переключился с использования Thread.CurrentPrincipal.Identity на HttpContext.Current.User.Identity, безрезультатно, и попробовал несколько других настроек. Любопытно, что единственное изменение, которое я смог сделать, сделав вмятину, было перенесено в рамки .net 4 (с 3.5). Проблема стала на порядок хуже. Также попробовал различные конфигурации балансировки нагрузки и смог исключить несколько конфигураций машины - тот же самый компьютер, который выпустил файл cookie, позже отклонил его.
Повторить итерацию симптомов: у нас были определенные пользователи (недетерминированные), которые порой не смогли бы использовать наш веб-сайт в аутентифицированном режиме (также не детерминированном). Милый.
К настоящему времени группа продуктов в Редмонде была вовлечена, а также в тупик. Но они придумали предложение, которое в конечном итоге решило проблему, здесь говорится:
Попробуйте заставить среду Microsoft.net использовать куки в качестве режима аутентификации, а не использовать cookieless state:
<authentication mode="Forms">
<forms cookieless="UseCookies" />
</authentication>
Перевернул переключатель, и мгновенно закончились проблемные запросы (как только я закончил сессию в нечетном состоянии).
Пока я рад, что проблема была решена - или, скорее, работала, - это не детерминированная обработка различных браузеров, которые меня беспокоят. Почему инфраструктура .net видит браузер однажды и обрабатывает его как поддерживающие файлы cookie, а позже в тот же день скажет, что он должен использовать cookieless сессию?
Моя последняя забота о том, сколько других сайтов там теряют 2% их аутентифицированного трафика, не зная? Учитывая исправление, он очень похож на ошибку в инфраструктуре .net 4.
Ответ 2
Не используйте это в аутентификации ASP.NET:
Thread.CurrentPrincipal = principal;
Вместо этого используйте:
HttpContext.Current.User = principal;
Подробнее см. в этой статье текст ссылки, но, что более важно, комментарии, включая первый и последний, - статья старая 2004, но аргументы все еще действуют сегодня.
Следовательно, вместо:
Thread.CurrentPrincipal.Identity.IsAuthenticated; // false
Thread.CurrentPrincipal.Identity.Name; // null (or empty, unsure off hand)
Вы должны использовать:
HttpContext.Current.User.Identity...
В этом случае вы также можете использовать AuthenticateRequest вместо PostAuthenticateRequest, потому что нет проблемы с потоком для обхода.
Ответ 3
Я бы порекомендовал вам начать с проверки того, что входящий веб-запрос фактически имеет cookie проверки подлинности и что он действителен. Модуль проверки подлинности форм принимает входящий веб-запрос и ищет, имеет ли он файл cookie для проверки подлинности. Если это так, то он пытается его декодировать, а затем генерировать принцип, используя его содержащееся значение. Поскольку ваш принцип пуст, это означает, что либо не было cookie аутентификации, содержащегося в запросе, либо было декодирование ошибок, либо оно было декодировано и признано, что оно истекло или оно было декодировано, и, как ни странно, в качестве имени пользователя имеет пустую строку.
Я рекомендую вам создать свой собственный модуль, который вызывается перед другими, и вы сами проверяете, присутствует ли файл cookie, а затем, если он декодирует его, чтобы увидеть, что он содержит. Это даст вам более подробную информацию о том, что происходит. Ваш собственный модуль для повторного использования методов из модуля FormsAuthentication для выполнения этих действий. Такие, как....
ForsmAuthentication.Decrypt(...)
Ответ 4
Вот аутентификация, которую я использую для большинства моих приложений ASP.NET/Mvc, для которых требуется базовая аутентификация. Я считаю, что это поможет и позволит вам хранить дополнительные данные в AuthenticationCookie. Если вы используете это раньше .Net делает свою аутентификацию .Net переопределит вашу настройку текущих основных и пользовательских свойств, использование PostAuthenticateRequest исправит главную проблему. Дайте мне знать, если у вас есть вопросы.
// Code that goes in the Global.asax.cs
// that runs after .Net has done it authentication
protected void Application_PostAuthenticateRequest(object sender, EventArgs e)
{
IPrincipal user = HttpContext.Current.User;
if (user == null) { return; }
FormsIdentity formsIdentity = user.Identity as FormsIdentity;
if (formsIdentity == null || !formsIdentity.IsAuthenticated) { return; }
Principal principal = new Principal(new Identity(formsIdentity.Ticket));
HttpContext.Current.User = principal;
Thread.CurrentPrincipal = principal;
}
// Base implementation of the System.Security.Principal.IPrincipal interface.
public class Principal : System.Security.Principal.IPrincipal
{
#region Fields
private IIdentity _identity = null;
#endregion
#region Constructors
public Principal(IIdentity identity)
{
_identity = identity;
}
#endregion
#region Properties
public IIdentity Identity
{
get
{
return _identity;
}
}
#endregion
#region Methods
public bool IsInRole(string role)
{
return (_identity != null && _identity.IsAuthenticated);
}
#endregion
}
// Base implementation of the System.Security.Principal.IIdentity interface.
public class Identity : System.Security.Principal.IIdentity
{
#region Fields
private readonly int _userId;
private readonly bool _isAuthenticated;
private readonly string _userName;
#endregion
#region Constructors
public Identity(FormsAuthenticationTicket formsAuthTicket)
{
if (formsAuthTicket == null)
{
throw new NullReferenceException("FormsAuthenticationTicket may not be null.");
}
if (string.IsNullOrEmpty(formsAuthTicket.UserData))
{
throw new NullReferenceException("FormsAuthenticationTicket.UserData may not be null or empty.");
}
string[] userData = formsAuthTicket.UserData.Split(new[] {"|"}, StringSplitOptions.RemoveEmptyEntries);
if (userData.Length < 1)
{
throw new ArgumentOutOfRangeException("formsAuthTicket", userData, "UserData does not contain a UserId and or a SiteId");
}
_userId = Convert.ToInt32(userData[0]);
_isAuthenticated = !formsAuthTicket.Expired;
_userName = formsAuthTicket.Name;
}
#endregion
#region Properties
public int UserId
{
get
{
return _userId;
}
}
public string AuthenticationType
{
get
{
return "Forms";
}
}
public bool IsAuthenticated
{
get
{
return _isAuthenticated;
}
}
public string Name
{
get
{
return _userName;
}
}
#endregion
}
Надеюсь, это поможет решить вашу проблему.
Ответ 5
re bounty:
Я вижу это в приложении Azure с asp.net 4.0, здесь есть файл cookie, и вы можете расшифровать его с помощью FormsAuthentication.Decrypt и прочитать все (не истекшее, правильное значение и т.д. и т.д.); как описано в ОП. В отличие от сценария OP, я уже использовал UseCookies и все равно получаю то же самое. Bounty для конкретной причины в asp.net, который может вызвать эту проблему и решение.
Мое дело было такой глупой проблемой:
- Выдавать токены через
FormsAuthentication.SetAuthCookie
и соответствующую переадресацию
- Настройте тег
<forms>
в файле web.config
- Проверьте
User.Identity.IsAuthenticated
на странице /false
- Файл cookie существует, и вы даже можете его расшифровать с помощью
FormsAuthentication.Decrypt
проблема: не забудьте установить <authentication mode="Forms">
, lol, total fail...
Мои 2 цента на OP: некоторые внутренние ссылки/переадресации/js могут разрушить хаос при попытке использовать cookieless авторизацию. Кроме того, обнаружение поддержки файлов cookie не очень точно.
Ответ 6
Я полагаю, исходя из моего прошлого опыта, что эта проблема на самом деле вызвана тем, что ASP.NET не распознает IE10+. Не известный браузер и его возможности, он возвращается к аутентификации без cookie (плохое или, по крайней мере, устаревшее предположение).
Обновления для файлов определения браузера были выпущены здесь. Проблема отсутствует с .NET 4.5
https://support.microsoft.com/kb/2600088
http://support.microsoft.com/kb/2600100
http://support.microsoft.com/kb/2608565