Conferma dell'account e ripristino della password con identità ASP.NET (C#)
Prima di eseguire questa esercitazione, è necessario completare creare un'app Web di MVC 5 sicura ASP.NET con accesso, conferma tramite posta elettronica e reimpostazione della password. Questa esercitazione contiene altri dettagli e illustra come configurare la posta elettronica per la conferma dell'account locale e consentire agli utenti di reimpostare la password dimenticata in ASP.NET Identity.
Un account utente locale richiede all'utente di creare una password per l'account e tale password viene archiviata (in modo sicuro) nell'app Web. ASP.NET Identity supporta anche gli account di social networking, che non richiedono all'utente di creare una password per l'app. Gli account di social networking usano terze parti (ad esempio Google, Twitter, Facebook o Microsoft) per autenticare gli utenti. Questo argomento illustra quanto segue:
- Creare un'app MVC ASP.NET ed esplorare le funzionalità di identità ASP.NET.
- Compilare l'esempio di identità
- Configurare la conferma tramite posta elettronica
I nuovi utenti registrano l'alias di posta elettronica, che crea un account locale.
Selezionando il pulsante Registra viene inviato un messaggio di posta elettronica di conferma contenente un token di convalida all'indirizzo di posta elettronica.
L'utente riceve un messaggio di posta elettronica con un token di conferma per il proprio account.
Selezionando il collegamento viene confermato l'account.
Ripristino/reimpostazione della password
Gli utenti locali che dimenticano la password possono avere un token di sicurezza inviato al proprio account di posta elettronica, consentendo loro di reimpostare la password.
L'utente riceverà presto un messaggio di posta elettronica con un collegamento che consente di reimpostare la password.
Selezionando il collegamento, verrà visualizzata la pagina Reimposta.
Selezionando il pulsante Reimposta si verifica che la password sia stata reimpostata.
Creare un'app Web ASP.NET
Per iniziare, installare ed eseguire Visual Studio 2017.
Creare un nuovo progetto Web ASP.NET e selezionare il modello MVC. Web Forms supportano anche ASP.NET Identity, quindi è possibile seguire passaggi simili in un'app Web form.
Modificare l'autenticazione in Account utente singoli.
Eseguire l'app, selezionare il collegamento Registra e registrare un utente. A questo punto, l'unica convalida nel messaggio di posta elettronica è con l'attributo [EmailAddress].
In Esplora server passare a Connessioni dati\DefaultConnection\Tables\AspNetUsers, fare clic con il pulsante destro del mouse e scegliere Apri definizione tabella.
L'immagine seguente mostra lo
AspNetUsers
schema:Fare clic con il pulsante destro del mouse sulla tabella AspNetUsers e scegliere Mostra dati tabella.
A questo punto il messaggio di posta elettronica non è stato confermato.
L'archivio dati predefinito per ASP.NET Identity è Entity Framework, ma è possibile configurarlo per l'uso di altri archivi dati e per aggiungere altri campi. Vedere la sezione Risorse aggiuntive alla fine di questa esercitazione.
La classe di avvio OWIN ( Startup.cs ) viene chiamata all'avvio dell'app e richiama il ConfigureAuth
metodo in App_Start\Startup.Auth.cs, che configura la pipeline OWIN e inizializza ASP.NET Identity. Esaminare il metodo ConfigureAuth
. Ogni CreatePerOwinContext
chiamata registra un callback (salvato in OwinContext
) che verrà chiamato una volta per ogni richiesta per creare un'istanza del tipo specificato. È possibile impostare un punto di interruzione nel costruttore e Create
nel metodo di ogni tipo (ApplicationDbContext, ApplicationUserManager
) e verificare che vengano chiamati in ogni richiesta. Un'istanza di ApplicationDbContext
e ApplicationUserManager
viene archiviata nel contesto OWIN, accessibile in tutta l'applicazione. ASP.NET l'identità si collega alla pipeline OWIN tramite il middleware dei cookie. Per altre informazioni, vedere Gestione della durata delle richieste per la classe UserManager in ASP.NET Identity.
Quando si modifica il profilo di sicurezza, viene generato e archiviato un nuovo timbro di sicurezza nel SecurityStamp
campo della tabella AspNetUsers . Si noti che il SecurityStamp
campo è diverso dal cookie di sicurezza. Il cookie di sicurezza non viene archiviato nella AspNetUsers
tabella (o in qualsiasi altro punto del database Di identità). Il token del cookie di sicurezza è autofirmato usando DPAPI e viene creato con le informazioni sull'ora UserId, SecurityStamp
di scadenza e .
Il middleware del cookie controlla il cookie in ogni richiesta. Il SecurityStampValidator
metodo nella Startup
classe raggiunge periodicamente il database e controlla periodicamente il timbro di sicurezza, come specificato con .validateInterval
Ciò si verifica solo ogni 30 minuti (nell'esempio) a meno che non si modififi il profilo di sicurezza. L'intervallo di 30 minuti è stato scelto per ridurre al minimo i viaggi al database. Per altri dettagli, vedere l'esercitazione sull'autenticazione a due fattori .
In base ai commenti nel codice, il UseCookieAuthentication
metodo supporta l'autenticazione dei cookie. Il SecurityStamp
campo e il codice associato forniscono un ulteriore livello di sicurezza per l'app, quando si modifica la password, si verrà disconnessi dal browser con cui si è connessi. Il SecurityStampValidator.OnValidateIdentity
metodo consente all'app di convalidare il token di sicurezza quando l'utente accede, che viene usato quando si modifica una password o si usa l'account di accesso esterno. Ciò è necessario per garantire che tutti i token (cookie) generati con la vecchia password vengano invalidati. Nel progetto di esempio, se si modifica la password degli utenti, viene generato un nuovo token per l'utente, tutti i token precedenti vengono invalidati e il SecurityStamp
campo viene aggiornato.
Il sistema di identità consente di configurare l'app in modo che gli utenti cambino il profilo di sicurezza (ad esempio, quando l'utente modifica la password o modifica l'account di accesso associato( ad esempio da Facebook, Google, account Microsoft e così via), l'utente viene disconnesso da tutte le istanze del browser. Ad esempio, l'immagine seguente mostra l'app di esempio Single Signout , che consente all'utente di disconnettersi da tutte le istanze del browser (in questo caso, Internet Explorer, Firefox e Chrome) selezionando un pulsante. In alternativa, l'esempio consente di disconnettersi solo da un'istanza del browser specifica.
L'app di esempio Single Signout mostra come ASP.NET Identity consente di rigenerare il token di sicurezza. Ciò è necessario per garantire che tutti i token (cookie) generati con la vecchia password vengano invalidati. Questa funzionalità offre un ulteriore livello di sicurezza per l'applicazione; quando si modifica la password, si verrà disconnessi in cui si è connessi all'applicazione.
Il file App_Start\IdentityConfig.cs contiene le ApplicationUserManager
classi , EmailService
e SmsService
. Le EmailService
classi e SmsService
implementano l'interfaccia IIdentityMessageService
, quindi sono disponibili metodi comuni in ogni classe per configurare posta elettronica e SMS. Anche se questa esercitazione illustra solo come aggiungere una notifica tramite SendGrid, è possibile inviare messaggi di posta elettronica usando SMTP e altri meccanismi.
La Startup
classe contiene anche la piastra caldaia per aggiungere account di accesso social (Facebook, Twitter e così via), vedi la mia esercitazione MVC 5 App con Facebook, Twitter, LinkedIn e Google OAuth2 Sign-On per altre info.
Esaminare la ApplicationUserManager
classe , che contiene le informazioni sull'identità degli utenti e configura le funzionalità seguenti:
- Requisiti di complessità della password.
- Blocco utente (tentativi e tempo).
- Autenticazione a due fattori (2FA). In un'altra esercitazione verranno illustrati 2FA e SMS.
- Associazione dei servizi di posta elettronica e SMS. (I'll cover SMS in another tutorial).
La ApplicationUserManager
classe deriva dalla classe generica UserManager<ApplicationUser>
. ApplicationUser
deriva da IdentityUser. IdentityUser
deriva dalla classe generica IdentityUser
:
// Default EntityFramework IUser implementation
public class IdentityUser<TKey, TLogin, TRole, TClaim> : IUser<TKey>
where TLogin : IdentityUserLogin<TKey>
where TRole : IdentityUserRole<TKey>
where TClaim : IdentityUserClaim<TKey>
{
public IdentityUser()
{
Claims = new List<TClaim>();
Roles = new List<TRole>();
Logins = new List<TLogin>();
}
/// User ID (Primary Key)
public virtual TKey Id { get; set; }
public virtual string Email { get; set; }
public virtual bool EmailConfirmed { get; set; }
public virtual string PasswordHash { get; set; }
/// A random value that should change whenever a users credentials have changed (password changed, login removed)
public virtual string SecurityStamp { get; set; }
public virtual string PhoneNumber { get; set; }
public virtual bool PhoneNumberConfirmed { get; set; }
public virtual bool TwoFactorEnabled { get; set; }
/// DateTime in UTC when lockout ends, any time in the past is considered not locked out.
public virtual DateTime? LockoutEndDateUtc { get; set; }
public virtual bool LockoutEnabled { get; set; }
/// Used to record failures for the purposes of lockout
public virtual int AccessFailedCount { get; set; }
/// Navigation property for user roles
public virtual ICollection<TRole> Roles { get; private set; }
/// Navigation property for user claims
public virtual ICollection<TClaim> Claims { get; private set; }
/// Navigation property for user logins
public virtual ICollection<TLogin> Logins { get; private set; }
public virtual string UserName { get; set; }
}
Le proprietà precedenti coincidono con le proprietà della AspNetUsers
tabella, illustrate in precedenza.
Gli argomenti generici su IUser
consentono di derivare una classe usando tipi diversi per la chiave primaria. Vedere l'esempio ChangePK che mostra come modificare la chiave primaria da stringa a int o GUID.
ApplicationUser
ApplicationUser
(public class ApplicationUserManager : UserManager<ApplicationUser>
) è definito in Models\IdentityModels.cs come segue:
public class ApplicationUser : IdentityUser
{
public async Task<ClaimsIdentity> GenerateUserIdentityAsync(
UserManager<ApplicationUser> manager)
{
// Note the authenticationType must match the one defined in
// CookieAuthenticationOptions.AuthenticationType
var userIdentity = await manager.CreateIdentityAsync(this,
DefaultAuthenticationTypes.ApplicationCookie);
// Add custom user claims here
return userIdentity;
}
}
Il codice evidenziato sopra genera un claimsIdentity. ASP.NET l'identità e l'autenticazione cookie OWIN sono basate sulle attestazioni, pertanto il framework richiede che l'app generi un per ClaimsIdentity
l'utente. ClaimsIdentity
contiene informazioni su tutte le attestazioni per l'utente, ad esempio il nome dell'utente, l'età e i ruoli a cui appartiene l'utente. È anche possibile aggiungere altre attestazioni per l'utente in questa fase.
Il metodo OWIN AuthenticationManager.SignIn
passa all'oggetto ClaimsIdentity
e accede all'utente:
private async Task SignInAsync(ApplicationUser user, bool isPersistent)
{
AuthenticationManager.SignOut(DefaultAuthenticationTypes.ExternalCookie);
AuthenticationManager.SignIn(new AuthenticationProperties(){
IsPersistent = isPersistent },
await user.GenerateUserIdentityAsync(UserManager));
}
L'app MVC 5 con Facebook, Twitter, LinkedIn e Google OAuth2 Sign-On mostra come aggiungere altre proprietà alla ApplicationUser
classe.
Email conferma
È consigliabile confermare il messaggio di posta elettronica di un nuovo utente registrato con per verificare che non siano rappresentazioni di un altro utente (ovvero non sono state registrate con un messaggio di posta elettronica di un altro utente). Si supponga di avere un forum di discussione, si vuole impedire "bob@example.com"
la registrazione come "joe@contoso.com"
. Senza conferma di posta elettronica, "joe@contoso.com"
potrebbe ricevere un messaggio di posta elettronica indesiderato dall'app. Si supponga che Bob abbia registrato accidentalmente come "bib@example.com"
e non lo avesse notato, non sarebbe in grado di usare il ripristino della password perché l'app non ha il suo messaggio di posta elettronica corretto. Email conferma offre solo una protezione limitata dai bot e non fornisce protezione da spammer determinati, hanno molti alias di posta elettronica funzionanti che possono usare per registrare. Nell'esempio seguente l'utente non sarà in grado di modificare la password fino a quando il proprio account non è stato confermato (selezionando un collegamento di conferma ricevuto nell'account di posta elettronica registrato). È possibile applicare questo flusso di lavoro ad altri scenari, ad esempio inviando un collegamento per confermare e reimpostare la password nei nuovi account creati dall'amministratore, inviando all'utente un messaggio di posta elettronica quando hanno modificato il proprio profilo e così via. In genere si vuole impedire ai nuovi utenti di pubblicare dati nel sito Web prima che siano stati confermati tramite posta elettronica, un messaggio di testo SMS o un altro meccanismo.
Creare un esempio più completo
In questa sezione si userà NuGet per scaricare un esempio più completo con cui verrà usato.
Creare un nuovo progetto Web di ASP.NET vuoto .
Nella console di Gestione pacchetti immettere i comandi seguenti:
Install-Package SendGrid Install-Package -Prerelease Microsoft.AspNet.Identity.Samples
In questa esercitazione si userà SendGrid per inviare un messaggio di posta elettronica. Il
Identity.Samples
pacchetto installa il codice con cui verrà lavorato.Impostare il progetto per l'uso di SSL.
Testare la creazione dell'account locale eseguendo l'app, selezionando il collegamento Registra e registrando il modulo di registrazione.
Selezionare il collegamento di posta elettronica demo, che simula la conferma della posta elettronica.
Rimuovere il codice di conferma del collegamento di posta elettronica demo dall'esempio (Il
ViewBag.Link
codice nel controller dell'account. Vedere i metodi di azione eForgotPasswordConfirmation
le visualizzazioniDisplayEmail
razor .
Avviso
Se si modifica una delle impostazioni di sicurezza in questo esempio, le app di produzione dovranno eseguire un controllo di sicurezza che chiama in modo esplicito le modifiche apportate.
Esaminare il codice in App_Start\IdentityConfig.cs
L'esempio illustra come creare un account e aggiungerlo al ruolo di Amministrazione. È consigliabile sostituire il messaggio di posta elettronica nell'esempio con il messaggio di posta elettronica usato per l'account amministratore. Il modo più semplice per creare un account amministratore è a livello di codice nel Seed
metodo. Si spera di avere uno strumento in futuro che consente di creare e amministrare utenti e ruoli. Il codice di esempio consente di creare e gestire utenti e ruoli, ma è prima necessario disporre di un account amministratore per eseguire i ruoli e le pagine di amministrazione utente. In questo esempio viene creato l'account amministratore quando il database viene inizializzato.
Modificare la password e modificare il nome in un account in cui è possibile ricevere notifiche tramite posta elettronica.
Avviso
Sicurezza: non archiviare mai i dati sensibili nel codice sorgente.
Come accennato in precedenza, la app.CreatePerOwinContext
chiamata nella classe di avvio aggiunge callback al Create
metodo del contenuto del database app, gestione utenti e classi manger di ruolo. La pipeline OWIN chiama il metodo in queste classi per ogni richiesta e archivia il Create
contesto per ogni classe. Il controller dell'account espone il gestore utenti dal contesto HTTP (che contiene il contesto OWIN):
public ApplicationUserManager UserManager
{
get
{
return _userManager ??
HttpContext.GetOwinContext().GetUserManager<ApplicationUserManager>();
}
private set
{
_userManager = value;
}
}
Quando un utente registra un account locale, viene chiamato il HTTP Post Register
metodo:
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<ActionResult> Register(RegisterViewModel model)
{
if (ModelState.IsValid)
{
var user = new ApplicationUser { UserName = model.Email, Email = model.Email };
var result = await UserManager.CreateAsync(user, model.Password);
if (result.Succeeded)
{
var code = await UserManager.GenerateEmailConfirmationTokenAsync(user.Id);
var callbackUrl = Url.Action(
"ConfirmEmail", "Account",
new { userId = user.Id, code = code },
protocol: Request.Url.Scheme);
await UserManager.SendEmailAsync(user.Id,
"Confirm your account",
"Please confirm your account by clicking this link: <a href=\""
+ callbackUrl + "\">link</a>");
// ViewBag.Link = callbackUrl; // Used only for initial demo.
return View("DisplayEmail");
}
AddErrors(result);
}
// If we got this far, something failed, redisplay form
return View(model);
}
Il codice precedente usa i dati del modello per creare un nuovo account utente usando il messaggio di posta elettronica e la password immessi. Se l'alias di posta elettronica si trova nell'archivio dati, la creazione dell'account ha esito negativo e il modulo viene visualizzato di nuovo. Il GenerateEmailConfirmationTokenAsync
metodo crea un token di conferma sicuro e lo archivia nell'archivio dati identity ASP.NET. Il metodo Url.Action crea un collegamento contenente il UserId
token di conferma e. Questo collegamento viene quindi inviato tramite posta elettronica all'utente, l'utente può selezionare il collegamento nell'app di posta elettronica per confermare il proprio account.
Configurare la conferma della posta elettronica
Passare alla pagina di iscrizione di SendGrid e registrarsi per l'account gratuito. Aggiungere codice simile al seguente per configurare SendGrid:
public class EmailService : IIdentityMessageService
{
public Task SendAsync(IdentityMessage message)
{
return configSendGridasync(message);
}
private Task configSendGridasync(IdentityMessage message)
{
var myMessage = new SendGridMessage();
myMessage.AddTo(message.Destination);
myMessage.From = new System.Net.Mail.MailAddress(
"Joe@contoso.com", "Joe S.");
myMessage.Subject = message.Subject;
myMessage.Text = message.Body;
myMessage.Html = message.Body;
var credentials = new NetworkCredential(
ConfigurationManager.AppSettings["mailAccount"],
ConfigurationManager.AppSettings["mailPassword"]
);
// Create a Web transport for sending email.
var transportWeb = new Web(credentials);
// Send the email.
if (transportWeb != null)
{
return transportWeb.DeliverAsync(myMessage);
}
else
{
return Task.FromResult(0);
}
}
}
Nota
Email client accettano spesso solo messaggi di testo (nessun html). È necessario specificare il messaggio in testo e HTML. Nell'esempio di SendGrid precedente, questa operazione viene eseguita con il myMessage.Text
codice e myMessage.Html
illustrato in precedenza.
Il codice seguente illustra come inviare messaggi di posta elettronica usando la classe MailMessage in cui message.Body
restituisce solo il collegamento.
void sendMail(Message message)
{
#region formatter
string text = string.Format("Please click on this link to {0}: {1}", message.Subject, message.Body);
string html = "Please confirm your account by clicking this link: <a href=\"" + message.Body + "\">link</a><br/>";
html += HttpUtility.HtmlEncode(@"Or click on the copy the following link on the browser:" + message.Body);
#endregion
MailMessage msg = new MailMessage();
msg.From = new MailAddress("joe@contoso.com");
msg.To.Add(new MailAddress(message.Destination));
msg.Subject = message.Subject;
msg.AlternateViews.Add(AlternateView.CreateAlternateViewFromString(text, null, MediaTypeNames.Text.Plain));
msg.AlternateViews.Add(AlternateView.CreateAlternateViewFromString(html, null, MediaTypeNames.Text.Html));
SmtpClient smtpClient = new SmtpClient("smtp.gmail.com", Convert.ToInt32(587));
System.Net.NetworkCredential credentials = new System.Net.NetworkCredential("joe@contoso.com", "XXXXXX");
smtpClient.Credentials = credentials;
smtpClient.EnableSsl = true;
smtpClient.Send(msg);
}
Avviso
Sicurezza: non archiviare mai i dati sensibili nel codice sorgente. L'account e le credenziali vengono archiviati nell'appSetting. In Azure è possibile archiviare questi valori in modo sicuro nella scheda Configura nella portale di Azure. Vedere Procedure consigliate per la distribuzione di password e altri dati sensibili in ASP.NET e Azure.
Immettere le credenziali di SendGrid, eseguire l'app, registrare con un alias di posta elettronica può selezionare il collegamento conferma nel messaggio di posta elettronica. Per informazioni su come eseguire questa operazione con l'account di posta elettronica Outlook.com , vedere Configurazione SMTP C# di John Atten per Outlook.Com host SMTP e il relativo ASP.NETIdentity 2.0: Configurazione della convalida dell'account e post di autorizzazione Two-Factor.
Dopo che un utente seleziona il pulsante Registra un messaggio di posta elettronica di conferma contenente un token di convalida viene inviato all'indirizzo di posta elettronica.
L'utente viene inviato un messaggio di posta elettronica con un token di conferma per il proprio account.
Esaminare il codice
Nel codice seguente viene illustrato il metodo POST ForgotPassword
.
public async Task<ActionResult> ForgotPassword(ForgotPasswordViewModel model)
{
if (ModelState.IsValid)
{
var user = await UserManager.FindByNameAsync(model.Email);
if (user == null || !(await UserManager.IsEmailConfirmedAsync(user.Id)))
{
// Don't reveal that the user does not exist or is not confirmed
return View("ForgotPasswordConfirmation");
}
var code = await UserManager.GeneratePasswordResetTokenAsync(user.Id);
var callbackUrl = Url.Action("ResetPassword", "Account",
new { UserId = user.Id, code = code }, protocol: Request.Url.Scheme);
await UserManager.SendEmailAsync(user.Id, "Reset Password",
"Please reset your password by clicking here: <a href=\"" + callbackUrl + "\">link</a>");
return View("ForgotPasswordConfirmation");
}
// If we got this far, something failed, redisplay form
return View(model);
}
Il metodo ha esito negativo in modo invisibile se il messaggio di posta elettronica dell'utente non è stato confermato. Se è stato inviato un errore per un indirizzo di posta elettronica non valido, gli utenti malintenzionati potrebbero usare tali informazioni per trovare userId validi (alias di posta elettronica) da attaccare.
Il codice seguente mostra il metodo nel controller dell'account chiamato quando l'utente seleziona il ConfirmEmail
collegamento di conferma nel messaggio di posta elettronica inviato:
public async Task<ActionResult> ConfirmEmail(string userId, string code)
{
if (userId == null || code == null)
{
return View("Error");
}
var result = await UserManager.ConfirmEmailAsync(userId, code);
if (result.Succeeded)
{
return View("ConfirmEmail");
}
AddErrors(result);
return View();
}
Una volta usato un token password dimenticato, viene invalidato. Il codice seguente cambia nel Create
metodo (nel file App_Start\IdentityConfig.cs ) imposta i token da scadere in 3 ore.
if (dataProtectionProvider != null)
{
manager.UserTokenProvider =
new DataProtectorTokenProvider<ApplicationUser>
(dataProtectionProvider.Create("ASP.NET Identity"))
{
TokenLifespan = TimeSpan.FromHours(3)
};
}
Con il codice precedente, la password dimenticata e i token di conferma della posta elettronica scadono in 3 ore. Il valore predefinito TokenLifespan
è un giorno.
Il codice seguente mostra il metodo di conferma della posta elettronica:
// GET: /Account/ConfirmEmail
[AllowAnonymous]
public async Task<ActionResult> ConfirmEmail(string userId, string code)
{
if (userId == null || code == null)
{
return View("Error");
}
IdentityResult result;
try
{
result = await UserManager.ConfirmEmailAsync(userId, code);
}
catch (InvalidOperationException ioe)
{
// ConfirmEmailAsync throws when the userId is not found.
ViewBag.errorMessage = ioe.Message;
return View("Error");
}
if (result.Succeeded)
{
return View();
}
// If we got this far, something failed.
AddErrors(result);
ViewBag.errorMessage = "ConfirmEmail failed";
return View("Error");
}
Per rendere l'app più sicura, ASP.NET Identity supporta l'autenticazione Two-Factor (2FA). Vedere ASP.NET Identity 2.0: Configurazione della convalida dell'account e Two-Factor autorizzazione di John Atten. Anche se è possibile impostare il blocco dell'account in caso di errori di tentativi di password di accesso, questo approccio rende l'accesso soggetto ai blocchi DOS . È consigliabile usare il blocco dell'account solo con 2FA.
Risorse aggiuntive
- Panoramica dei provider di archiviazione personalizzati per ASP.NET Identity
- MVC 5 App con Facebook, Twitter, LinkedIn e Google OAuth2 Sign-On illustra anche come aggiungere informazioni sul profilo alla tabella utenti.
- ASP.NET MVC e Identity 2.0: Informazioni sulle nozioni di base di John Atten.
- Introduzione ad ASP.NET Identity
- Annuncio di RTM di ASP.NET Identity 2.0.0 da Pranav Rastogi.