Ошибка при использовании PrincipalContext.ValidateCredentials для аутентификации на локальном компьютере?
У меня есть служба WCF, которая содержит метод Login
, который проверяет имя пользователя и пароль на учетные данные локального компьютера, и после кажущегося случайного периода времени он перестанет работать для некоторых пользователей.
Действительная команда входа выглядит следующим образом:
public UserModel Login(string username, string password, string ipAddress)
{
// Verify valid parameters
if (username == null || password == null)
return null;
try
{
using (var pContext = new PrincipalContext(ContextType.Machine))
{
// Authenticate against local machine
if (pContext.ValidateCredentials(username, password))
{
// Authenticate user against database
using (var context = new MyEntities(Connections.GetConnectionString()))
{
var user = (from u in context.Users
where u.LoginName.ToUpper() == username.ToUpper()
&& u.IsActive == true
&& (string.IsNullOrEmpty(u.AllowedIpAddresses)
|| u.AllowedIpAddresses.Contains(ipAddress))
select u).FirstOrDefault();
// If user failed to authenticate against database
if (user == null)
return null;
// Map entity object to return object and assign session token
var userModel = Mapper.Map<User, UserModel>(user);
userModel.Token = Guid.NewGuid();
userModel.LastActivity = DateTime.Now;
// Authenticated users are added to a list on the server
// and their login expires after 20 minutes of inactivity
authenticatedUsers.Add(userModel);
sessionTimer.Start();
// User successfully authenticated, so return UserModel to client
return userModel;
}
}
}
}
catch(Exception ex)
{
// This is getting hit
MessageLog.WriteMessage(string.Format("Exception occurred while validating user: {0}\n{1}", ex.Message, ex.StackTrace));
return null;
}
// If authentication against local machine failed, return null
return null;
}
Кажется, что это работает отлично в течение нескольких дней, тогда он резко прекратит работу для некоторых пользователей и выбросит это исключение:
Не допускается несколько подключений к серверу или совместно используемому ресурсу одним и тем же пользователем, используя несколько имен пользователей. Отключите все предыдущие подключения к серверу или совместно используемому ресурсу и повторите попытку. (Исключение из HRESULT: 0x800704C3)
в System.DirectoryServices.AccountManagement.CredentialValidator.BindSam(String target, String userName, String password)
в System.DirectoryServices.AccountManagement.CredentialValidator.Validate(String userName, String password)
в System.DirectoryServices.AccountManagement.PrincipalContext.ValidateCredentials(String userName, String password)
в MyNamespace.LoginService.Login(имя пользователя String, String password, String ipAddress) в папке C:\Users\me\Desktop\somefolder\LoginService.svc.cs: строка 67
Линия 67: if (pContext.ValidateCredentials(username, password))
Я не уверен, имеет значение это или нет, но последняя строка сообщения об ошибке - это путь решения VS на моей машине разработки, а не путь к файлам на рабочем сервере.
Когда он терпит неудачу, для некоторых пользователей он терпит неудачу, в то время как другие могут продолжать логин. Единственное, что я нашел для временного исправления ошибки, - это запустить iisreset
. Остановка/запуск веб-сайта или переработка пула приложений не работают.
Я не могу воспроизвести ошибку по требованию. Я пробовал войти в систему с одним и тем же пользователем из нескольких сеансов и IP-адресов, одновременно вступая в систему с разными пользователями из одного сеанса браузера, рассылая спам кнопку "Войти", чтобы попытаться запустить ее несколько раз и т.д., Но все появляется чтобы нормально работать.
Из нашего ведения журнала видно, что пользователи успешно вошли в систему в прошлом:
3/21/2013
o 9:03a I logged in
o 1:54p UserB logged in
o 1:55p UserA logged in
o 2:38p UserB logged in
o 3:48p UserB logged in
o 5:18p UserA logged in
o 6:11p UserB logged in
3/22/2013
o 12:42p UserA logged in
o 5:22p UserB logged in
o 8:04p UserB logged in
3/25/2013 (today)
o 8:47a I logged in
o 12:38p UserB tries logging in and fails. Repeated ~15 times over next 45 min
o 1:58p I login successfully
o 2:08p I try to login with UserB login info and fail with the same error
Причина, по которой мы аутентифицируемся на локальном компьютере, заключается в том, что у пользователей есть учетная запись, созданная локально для доступа к FTP, и мы не хотим создавать собственную систему входа в систему или заставляем наших пользователей помнить два набора учетных данных.
Код должен только аутентифицировать учетные данные пользователя и не выполняет ничего другого с учетными данными пользователя. Нет другого кода, который использует System.DirectoryServices
, не будет запускаться IO файла и не будет доступа к чему-либо локально в файловой системе, кроме файлов, необходимых для запуска веб-приложения.
Что может вызвать появление этой ошибки, казалось бы, случайным образом через несколько дней? И как я могу это исправить?
Сервер - это Windows Server 2003, который запускает IIS 6.0, и он настроен на использование .Net Framework 4.0
Ответы
Ответ 1
Ближайший, который я могу найти в Интернете, чтобы объяснить эту проблему, - этот пост форума, где пользователь испытывает ту же ошибку и получил повторную запись
Поставщик WinNT не работает в серверной среде. я на самом деле удивителен, вы не видите этого с гораздо меньшей нагрузкой. у меня есть смог получить это только у 2 или 3 пользователей.
и этот комментарий СОСТОЯНИЯ, указав
Лучший способ правильно идентифицировать кого-то - использовать LogonUserAPI как пишет @stephbu. Все другие методы, описанные в этом сообщении, НЕ РАБОТА 100%
где "все другие методы" включают верхний проголосовавший ответ на использование PrincipalContext.ValidateCredentials
Его звучание как PrincipalContext.ValidateCredentials
не полностью на 100% надежное в Windows Server 2003 и IIS6.0, поэтому я переписал код аутентификации, чтобы использовать LogonUser вместо WinAPI.
[DllImport("advapi32.dll", SetLastError = true)]
public static extern bool LogonUser(
string lpszUsername,
string lpszDomain,
string lpszPassword,
int dwLogonType,
int dwLogonProvider,
out IntPtr phToken
);
IntPtr hToken;
if (LogonUser(username, "", password,
LOGON32_LOGON_NETWORK, LOGON32_PROVIDER_DEFAULT, out hToken))
{
...
}