2016-01-22 7 views
6

Sto seguendo lo bitoftech tutorial sulla creazione di attestazioni basate su identità e ruolo con JWT. L'utente dell'applicazione è una tabella utente personalizzata con PK int.CreateUserIdenityAsync restituisce l'eccezione "UserId non trovato" per IdentityUser personalizzato

Attualmente, il metodo GenerateUserIdentityAsync restituisce solo uno strano errore UserId not found. qui è il mio codice:

ClaimsIdentity oAuthIdentity = await user.GenerateUserIdentityAsync(userManager, "JWT"); 

e l'attuazione in User entità:

public async Task<ClaimsIdentity> GenerateUserIdentityAsync(UserManager<User, int> manager, string authenticationType) 
{ 
    //error on this line: CreateIdentityAsync throws error 
    var userIdentity = await manager.CreateIdentityAsync(this, authenticationType); 
    return userIdentity; 
} 

La mia classe UserManager è definita in questo modo:

public class AppUserManager : UserManager<User, int> 

Stranamente, quando il debug, l'istanza this in GenerateIdentityAsync ha una proprietà UserId, ma la base ha solo un id e mi chiedo se sia così e sta saltando fuori? (non suona bene)

Stavo guardando il source code (riga 80) ma non riesco a capire dove viene lanciata l'eccezione.

L'eccezione esatto essere gettato è:

UserId not found. 
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.InvalidOperationException: 
    UserId not found. 

E stack trace non è tutto ciò che utile (per me)

Come faccio a sapere il motivo per cui/in cui l'ID utente non è disponibile?


particolari modalità:

mio GrantResourceOwnerCredentials():

public override async Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context) 
{ 
    context.OwinContext.Response.Headers.Add("Access-Control-Allow-Origin", new[] {"*"}); 

    var userManager = context.OwinContext.GetUserManager<ApplicationUserManager>(); 
    User user = await userManager.FindAsync(context.UserName, context.Password); 

    if (user == null) // this is NOT null 
    { 
     context.SetError("invalid_grant", "The username or password is incorrect"); 
     return; 
    } 

    // this line fails 
    ClaimsIdentity oAuthIdentity = await user.GenerateUserIdentityAsync(userManager, "JWT"); 
    var ticket = new AuthenticationTicket(oAuthIdentity, null); 
    context.Validated(ticket); 
} 

E la ApplicationUser (che, nel mio caso, è solo User)

public partial class User : IdentityUser<int, CustomUserLogin, CustomUserRole, CustomUserClaim> 
{ 
    public int UserId { get; set; } 
    public string Fullname { get; set; } 
    public string Address { get; set; } 
    public string ContactNumber { get; set; } 
} 
+0

Verificare in aspnet db se si dispone dei nomi di colonna corretti (ID utente anziché ID o viceversa). Inoltre, una possibilità è quella di mancata corrispondenza del tipo di colonna (identificatori univoci rispetto a nvarchar (256)). – Batuta

risposta

3

Come hai scoperto durante il debug di IdentityUser ha un Id che nel tuo caso rappresenterebbe l'ID dell'utente.

è necessario rimuovere il UserId dalla classe User, utilizzare la base Id da IdentityUser e rinominare la colonna UserId nella tabella utente personalizzata per Id.

Qualsiasi proprietà nella classe User deve avere anche una colonna corrispondente nella tabella utente nel database. In caso contrario, si otterrà lo stesso errore per le proprietà che non corrispondono.

Ciò significherebbe Fullname, Address e ContactNumber deve avere corrispondenza nomi delle colonne nella tabella AspNetUsers altrimenti si otterrà lo stesso errore per quelle proprietà pure.

+0

ciao, ho apportato queste modifiche al mio, ma continuando a colpire lo stesso errore; Ora mi chiedo se 'UserId' è ciò che penso sia, non ci sono eccezioni interne, solo tracce di stack ma questo non è di grande aiuto, sfortunatamente. Il tuo codice sopra richiama l'utente due volte, il che non è un problema se funzionasse, potrei refactoring in seguito, ma sta ancora lanciando la stessa identica eccezione – LocustHorde

+0

@LocustHorde, sono tornato al tavolo da disegno e ho ricontrollato il tuo problema. Utilizzando le informazioni aggiuntive fornite, sono stato in grado di ricreare la stessa eccezione, compresa l'eccezione interna. 'UserId' deve essere una colonna valida nella tabella' AspNetUsers'. Penso che anche un commentatore precedente abbia indicato questo. – Nkosi

+0

Eccellente! Dopo il tuo commento ho notato che c'erano due colonne 'UserId' e' Id' nella tabella, e nel mio metodo seed stavo semplicemente compilando 'UserId', e' Id' era zero.Quando ho provato ad accedere, per qualsiasi ragione il metodo continuava a lanciare 'UserId' non è stato trovato. Una volta modificato manualmente lo zero in un numero intero, ha iniziato a funzionare correttamente! Quindi suppongo di dover rimuovere 'Id' e in qualche modo dire a Identity di usare' UserId' invece ... – LocustHorde

2

Che aspetto ha la classe ApplicationUser? Che aspetto ha questo metodo nella tua applicazione?

public override async Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context){} 

commenti di Taiseer su GrantResourceOwnerCredentials sono:

"stiamo costruendo un'identità per l'utente connesso, questa identità conterrà tutti i ruoli e le richieste per l'utente autenticato"

Ho dovuto aggiungere ClaimTypes.NameIdentifier a ClaimsIdentity per risolvere un problema simile. Qui è la parte importante del mio metodo GrantResourceOwnerCredentials:

ApplicationUser user = await userManager.FindAsync(context.UserName, context.Password); 

if (user == null) 
{ 
    context.SetError("invalid_grant", "The user name or password is incorrect."); 
    return; 
} 

var identity = new ClaimsIdentity(context.Options.AuthenticationType); 
identity.AddClaim(new Claim(ClaimTypes.NameIdentifier, user.Id.ToString())); 
+0

Ciao, ho aggiunto entrambi i blocchi di codice sopra; non stai usando JWT? – LocustHorde

+0

L'ho provato con 'DefaultAuthenticationTypes.ApplicationCookie' e ancora lo stesso errore, suppongo di poter escludere che JWT interferisca con esso almeno per ora – LocustHorde

+0

Corretto, non stavo usando JWT. – BBauer42

2

avete sia UserId e Id immobili a tua classe User - Id ereditato da IdentityUser. Il problema è che probabilmente hai configurato UserId come chiave primaria per User.

L'eccezione che si ottiene viene generata nel metodo ClaimsIdentityFactory.CreateAsync, su line 97UserManager.GetSecurityStampAsync. Come puoi vedere, user.Id utilizzato per recuperare un timbro di sicurezza.

Se si guarda dentro UserManager.GetSecurityStampAsync si vedrà che l'eccezione si ottiene viene lanciata esattamente qui:

public virtual async Task<string> GetSecurityStampAsync(TKey userId) 
{ 
    ThrowIfDisposed(); 
    var securityStore = GetSecurityStore(); 
    var user = await FindByIdAsync(userId).WithCurrentCulture(); 
    if (user == null) 
    { 
     throw new InvalidOperationException(String.Format(CultureInfo.CurrentCulture, Resources.UserIdNotFound, 
      userId)); 
    } 
    return await securityStore.GetSecurityStampAsync(user).WithCurrentCulture(); 
} 

Quindi, rimuovere UserId proprietà da User classe e iniziare a utilizzare Id (ereditato da IdentityUser), invece.

2

Ho affrontato lo stesso identico problema. Dopo tanto mal di testa, ho potuto risolvere il vero problema. Quando si modifica il tipo di dati della proprietà Id della classe User in stringa, un Guid(). ToString() viene assegnato alla proprietà Id nel costruttore e lo stesso valore viene salvato nel database e Identity recupera i dettagli dell'utente utilizzando tale valore.

Tuttavia, se il tipo di dati della proprietà Id è stato modificato su int e non ha fornito un valore per tale proprietà nel costruttore, Identity tenta ancora di recuperare i dettagli Utente utilizzando il valore int predefinito (0). cause genera il messaggio "System.InvalidOperationException: UserId non trovato".

Ho risolto questo recuperando il valore dal database tramite comando.ExecuteScalar() e assegnare il valore a user.Id. Spero che questo possa aiutare qualcuno che si trova ad affrontare un problema simile.

+0

Ciao @vkuttyp, di quale costruttore stai parlando? Grazie. – chemitaxis

+0

puoi condividere un po 'più dettagli? dove hai impostato esattamente quell'utente? grazie –

+0

Ciao @chemitaxis, il costruttore che ho menzionato è il costruttore della classe IdentityUser. – vkuttyp