2013-03-25 4 views
10

Ho un servizio WCF che contiene un metodo Login che convalida un nome utente e una password contro le credenziali del computer locale e, dopo un periodo apparentemente casuale, smetterà di funzionare per alcuni utenti.Errore durante l'utilizzo di PrincipalContext.ValidateCredentials per l'autenticazione su un computer locale?

Il comando login attuale assomiglia a questo:

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; 
} 

Questo sembra funzionare bene per alcuni giorni, poi si bruscamente smettere di lavorare per alcuni utenti e lanciare questa eccezione:

Non sono consentite più connessioni a un server o a una risorsa condivisa dallo stesso utente, utilizzando più di un nome utente. Disconnette tutte le connessioni precedenti al server o alla risorsa condivisa e riprova. (Eccezione da HRESULT: 0x800704C3)

a System.DirectoryServices.AccountManagement.CredentialValidator.BindSam (target String, String username, String password)

a System.DirectoryServices.AccountManagement.CredentialValidator.Validate (String username, string password)

a System.DirectoryServices.AccountManagement.PrincipalContext.ValidateCredentials (String username, String password)

a MyNamespace.LoginService.Login (string username, la password String, String ipAddress) in C: \ Users \ m e \ Desktop \ somefolder \ LoginService.svc.cs: linea 67

Linea 67 è: if (pContext.ValidateCredentials(username, password))

non sono sicuro se è importante o no, ma la linea finale del messaggio di errore è il percorso della soluzione VS sulla mia macchina di sviluppo, non il percorso dei file sul server di produzione.

Quando fallisce, fallisce solo per alcuni utenti, mentre altri possono continuare ad accedere bene. L'unica cosa che ho trovato per risolvere temporaneamente l'errore è in esecuzione iisreset. Arrestare/avviare il sito Web o riciclare il pool di app non funziona.

Non riesco a riprodurre l'errore su richiesta. Ho provato ad accedere con lo stesso utente da più sessioni e indirizzi IP, collegandomi contemporaneamente a utenti diversi dalla stessa sessione del browser, inviando spam al pulsante Accedi per cercare di farlo funzionare più volte, ecc. Ma tutto appare lavorare bene

posso vedere dalla nostra registrazione che gli utenti sono stati con successo in grado di accedere in passato:

 
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's login info and fail with the same error 

Il motivo per cui l'autenticazione della macchina locale è perché gli utenti hanno un account creato a livello locale per l'accesso FTP, e non volevamo creare il nostro sistema di login personalizzato o far ricordare ai nostri utenti due insiemi di credenziali.

Il codice deve solo autenticare le credenziali dell'utente e non fa nient'altro con le credenziali dell'utente. Non esiste un altro codice che utilizzi System.DirectoryServices, nessun file IO in corso e nessun accesso a qualcosa localmente sul file system diverso dai file necessari per eseguire l'applicazione web.

Cosa può causare quell'errore, apparentemente a caso, dopo alcuni giorni? E come posso ripararlo?

il server è Windows Server 2003, che viene eseguito IIS 6.0, ed è impostato per utilizzare .Net Framework 4.0

risposta

4

Il più vicino che posso trovare on-line verso spiegando questo problema è this forum post, in cui l'utente sperimentando lo stesso errore e ha ottenuto un replay affermando:

Il provider WinNT non funziona bene in un ambiente server. Sono davvero sorpreso che non vedi questo con un carico molto più piccolo. Ho stato in grado di ottenere questo con solo 2 o 3 utenti.

e this SO comment affermando

il modo migliore per autenticare correttamente qualcuno è quello di utilizzare LogonUserAPI come @stephbu scrivere. Tutti gli altri metodi descritti in questo post sarà NON LAVORO 100%

dove "tutti gli altri metodi" include la parte superiore ha votato risposta di utilizzare PrincipalContext.ValidateCredentials

Il suo suono come PrincipalContext.ValidateCredentials non è completamente affidabile al 100% su Windows Server 2003 e IIS6.0, quindi ho riscritto il mio codice di autenticazione per utilizzare invece il metodo WinAPI LogonUser.

[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)) 
{ 
    ... 
} 
+0

ho perso diverse ore, grazie rachel, –