Sdílet prostřednictvím


Vytvoření zabezpečené webové aplikace ASP.NET MVC 5 s přihlášením, potvrzením e-mailu a resetováním hesla (C#)

Rick Anderson

V tomto kurzu se dozvíte, jak vytvořit webovou aplikaci ASP.NET MVC 5 s potvrzením e-mailu a resetováním hesla pomocí systému členství ve službě ASP.NET Identity.

Aktualizovanou verzi tohoto kurzu, která používá .NET Core, najdete v tématu Potvrzení účtu a obnovení hesla v ASP.NET Core.

Vytvoření aplikace ASP.NET MVC

Začněte instalací a spuštěním sady Visual Studio Express 2013 pro web nebo Visual Studio 2013. Nainstalujte Visual Studio 2013 Update 3 nebo novější.

Poznámka:

Upozornění: K dokončení tohoto kurzu musíte nainstalovat Visual Studio 2013 Update 3 nebo vyšší.

  1. Vytvořte nový webový projekt ASP.NET a vyberte šablonu MVC. Webové formuláře také podporují ASP.NET Identity, takže můžete postupovat podobně v aplikaci webových formulářů.
    Snímek obrazovky znázorňující stránku Nový projekt S P s tečkou Šablona jazyka C jazyka M je vybraná a zvýrazní se jednotlivé uživatelské účty.

  2. Ponechte výchozí ověřování jako jednotlivé uživatelské účty. Pokud chcete aplikaci hostovat v Azure, nechte toto políčko zaškrtnuté. Později v tomto kurzu nasadíme do Azure. Účet Azure můžete otevřít zdarma.

  3. Nastavte projekt tak, aby používal PROTOKOL SSL.

  4. Spusťte aplikaci, klikněte na odkaz Zaregistrovat a zaregistrujte uživatele. V tomto okamžiku je jediným ověřením e-mailu atribut [EmailAddress].

  5. V Průzkumníku serveru přejděte na Datová připojení\DefaultConnection\Tables\AspNetUsers, klikněte pravým tlačítkem myši a vyberte Otevřít definici tabulky.

    Následující obrázek znázorňuje AspNetUsers schéma:

    Snímek obrazovky znázorňující kartu Soubor skriptu A S P Net v Průzkumníku serveru

  6. Klikněte pravým tlačítkem myši na tabulku AspNetUsers a vyberte Zobrazit data tabulky.
    Snímek obrazovky znázorňující schéma A S P Net Users Sloupec Potvrzeno e-mailem označený jako False je zvýrazněný.
    V tomto okamžiku se e-mail nepotvrdil.

  7. Klikněte na řádek a vyberte odstranit. Tento e-mail přidáte znovu v dalším kroku a odešlete potvrzovací e-mail.

E-mailové potvrzení

Osvědčeným postupem je potvrdit e-mail nové registrace uživatele a ověřit, že se nezosobní někdo jiný (to znamená, že se nezaregistroval v e-mailu někoho jiného). Předpokládejme, že jste měli diskuzní fórum, byste chtěli zabránit "bob@example.com" v registraci jako "joe@contoso.com". Bez potvrzení "joe@contoso.com" e-mailu může vaše aplikace dostávat nežádoucí e-maily. Předpokládejme, že Bob omylem zaregistroval jako "bib@example.com" a nevšimnul si ho, nemohl by použít obnovení hesla, protože aplikace nemá správný e-mail. Potvrzení e-mailu poskytuje pouze omezenou ochranu před roboty a neposkytuje ochranu před určenými spammery, mají mnoho pracovních e-mailových aliasů, které můžou použít k registraci.

Obecně chcete zabránit novým uživatelům v publikování jakýchkoli dat na váš web předtím, než je potvrdí e-mail, SMS textová zpráva nebo jiný mechanismus. V následujících částech povolíme potvrzení e-mailu a upravíme kód tak, aby se nově zaregistrovaní uživatelé nemohli přihlásit, dokud se jejich e-mail nepotvrdí.

Připojení SendGridu

Pokyny v této části nejsou aktuální. Aktualizované pokyny najdete v tématu Konfigurace poskytovatele e-mailu SendGrid.

I když tento kurz ukazuje, jak přidat e-mailové oznámení prostřednictvím SendGridu, můžete odesílat e-maily pomocí protokolu SMTP a dalších mechanismů (viz další zdroje informací).

  1. V konzole Správce balíčků zadejte následující příkaz:

    Install-Package SendGrid
    
  2. Přejděte na registrační stránku Azure SendGrid a zaregistrujte si bezplatný účet SendGrid. Nakonfigurujte SendGrid přidáním kódu podobného následujícímu v App_Start/IdentityConfig.cs:

    public class EmailService : IIdentityMessageService
    {
       public async Task SendAsync(IdentityMessage message)
       {
          await configSendGridasync(message);
       }
    
       // Use NuGet to install SendGrid (Basic C# client lib) 
       private async 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)
          {
             await transportWeb.DeliverAsync(myMessage);
          }
          else
          {
             Trace.TraceError("Failed to create Web transport.");
             await Task.FromResult(0);
          }
       }
    }
    

Budete muset přidat následující položky:

using SendGrid;
using System.Net;
using System.Configuration;
using System.Diagnostics;

Abychom tuto ukázku zachovali jednoduše, uložíme nastavení aplikace do souboru web.config :

</connectionStrings>
   <appSettings>
      <add key="webpages:Version" value="3.0.0.0" />
      <!-- Markup removed for clarity. -->
      
      <add key="mailAccount" value="xyz" />
      <add key="mailPassword" value="password" />
   </appSettings>
  <system.web>

Upozorňující

Zabezpečení – Do zdrojového kódu nikdy neukládáte citlivá data. Účet a přihlašovací údaje jsou uložené v appSetting. V Azure můžete tyto hodnoty bezpečně uložit na kartě Konfigurace na webu Azure Portal. Přečtěte si osvědčené postupy pro nasazení hesel a dalších citlivých dat do ASP.NET a Azure.

Povolení potvrzení e-mailu v kontroleru účtu

//
// POST: /Account/Register
[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)
        {
            await SignInManager.SignInAsync(user, isPersistent:false, rememberBrowser:false);

            string 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 <a href=\"" 
               + callbackUrl + "\">here</a>");

            return RedirectToAction("Index", "Home");
        }
        AddErrors(result);
    }

    // If we got this far, something failed, redisplay form
    return View(model);
}

Ověřte, že soubor Views\Account\ConfirmEmail.cshtml má správnou syntaxi razor. ( Znak @ v prvním řádku může chybět. )

@{
    ViewBag.Title = "Confirm Email";
}

<h2>@ViewBag.Title.</h2>
<div>
    <p>
        Thank you for confirming your email. Please @Html.ActionLink("Click here to Log in", "Login", "Account", routeValues: null, htmlAttributes: new { id = "loginLink" })
    </p>
</div>

Spusťte aplikaci a klikněte na odkaz Zaregistrovat. Po odeslání registračního formuláře se přihlásíte.

Snímek obrazovky znázorňující domovskou stránku Přihlášení k my A S P

Zkontrolujte svůj e-mailový účet a kliknutím na odkaz potvrďte svůj e-mail.

Před přihlášením vyžadovat potvrzení e-mailu

Jakmile uživatel dokončí registrační formulář, přihlásí se. Obvykle chcete před přihlášením potvrdit jejich e-maily. V následující části upravíme kód tak, aby před přihlášením (ověřením) museli mít noví uživatelé potvrzený e-mail. Aktualizujte metodu HttpPost Register následujícími zvýrazněnými změnami:

//
// POST: /Account/Register
[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)
      {
         //  Comment the following line to prevent log in until the user is confirmed.
         //  await SignInManager.SignInAsync(user, isPersistent:false, rememberBrowser:false);

         string 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 <a href=\"" + callbackUrl + "\">here</a>");

         // Uncomment to debug locally 
         // TempData["ViewBagLink"] = callbackUrl;

         ViewBag.Message = "Check your email and confirm your account, you must be confirmed "
                         + "before you can log in.";

         return View("Info");
         //return RedirectToAction("Index", "Home");
      }
      AddErrors(result);
   }

   // If we got this far, something failed, redisplay form
   return View(model);
}

Když metodu SignInAsync zakomentuje, uživatel nebude přihlášený registrací. Řádek TempData["ViewBagLink"] = callbackUrl; lze použít k ladění aplikace a otestování registrace bez odeslání e-mailu. ViewBag.Message slouží k zobrazení pokynů k potvrzení. Ukázka stahování obsahuje kód pro testování potvrzení e-mailu bez nastavení e-mailu a dá se také použít k ladění aplikace.

Views\Shared\Info.cshtml Vytvořte soubor a přidejte následující kód razor:

@{
   ViewBag.Title = "Info";
}
<h2>@ViewBag.Title.</h2>
<h3>@ViewBag.Message</h3>

Přidejte atribut Authorize do Contact metody akce kontroleru Home. Kliknutím na odkaz Kontakt můžete ověřit, že anonymní uživatelé nemají přístup a ověření uživatelé mají přístup.

[Authorize]
public ActionResult Contact()
{
   ViewBag.Message = "Your contact page.";

   return View();
}

Musíte také aktualizovat metodu HttpPost Login akce:

//
// POST: /Account/Login
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<ActionResult> Login(LoginViewModel model, string returnUrl)
{
    if (!ModelState.IsValid)
    {
        return View(model);
    }

    // Require the user to have a confirmed email before they can log on.
    var user = await UserManager.FindByNameAsync(model.Email);
    if (user != null)
    {
       if (!await UserManager.IsEmailConfirmedAsync(user.Id))
       {
          ViewBag.errorMessage = "You must have a confirmed email to log on.";
          return View("Error");
       }
    }

    // This doesn't count login failures towards account lockout
    // To enable password failures to trigger account lockout, change to shouldLockout: true
    var result = await SignInManager.PasswordSignInAsync(model.Email, model.Password, model.RememberMe, shouldLockout: false);
    switch (result)
    {
        case SignInStatus.Success:
            return RedirectToLocal(returnUrl);
        case SignInStatus.LockedOut:
            return View("Lockout");
        case SignInStatus.RequiresVerification:
            return RedirectToAction("SendCode", new { ReturnUrl = returnUrl, RememberMe = model.RememberMe });
        case SignInStatus.Failure:
        default:
            ModelState.AddModelError("", "Invalid login attempt.");
            return View(model);
    }
}

Aktualizujte zobrazení Views\Shared\Error.cshtml, aby se zobrazila chybová zpráva:

@model System.Web.Mvc.HandleErrorInfo

@{
    ViewBag.Title = "Error";
}

<h1 class="text-danger">Error.</h1>
@{
   if (String.IsNullOrEmpty(ViewBag.errorMessage))
   {
      <h2 class="text-danger">An error occurred while processing your request.</h2>
   }
   else
   {
      <h2 class="text-danger">@ViewBag.errorMessage</h2>
   }
}

Odstraňte všechny účty v tabulce AspNetUsers , které obsahují e-mailový alias, se kterým chcete testovat. Spusťte aplikaci a ověřte, že se nemůžete přihlásit, dokud nepotvrdíte svoji e-mailovou adresu. Jakmile potvrdíte svoji e-mailovou adresu, klikněte na odkaz Kontakt .

Obnovení nebo resetování hesla

Odeberte znaky komentáře z HttpPost ForgotPassword metody akce v kontroleru účtu:

//
// POST: /Account/ForgotPassword
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
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");
        }

        string 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 <a href=\"" + callbackUrl + "\">here</a>");
        return RedirectToAction("ForgotPasswordConfirmation", "Account");
    }

    // If we got this far, something failed, redisplay form
    return View(model);
}

Odeberte znaky komentáře z ForgotPassword actionLinku v souboru Zobrazení\Account\Login.cshtml razor view:

@using MvcPWy.Models
@model LoginViewModel
@{
   ViewBag.Title = "Log in";
}

<h2>@ViewBag.Title.</h2>
<div class="row">
   <div class="col-md-8">
      <section id="loginForm">
         @using (Html.BeginForm("Login", "Account", new { ReturnUrl = ViewBag.ReturnUrl }, FormMethod.Post, new { @class = "form-horizontal", role = "form" }))
         {
            @Html.AntiForgeryToken()
            <h4>Use a local account to log in.</h4>
            <hr />
            @Html.ValidationSummary(true, "", new { @class = "text-danger" })
            <div class="form-group">
               @Html.LabelFor(m => m.Email, new { @class = "col-md-2 control-label" })
               <div class="col-md-10">
                  @Html.TextBoxFor(m => m.Email, new { @class = "form-control" })
                  @Html.ValidationMessageFor(m => m.Email, "", new { @class = "text-danger" })
               </div>
            </div>
            <div class="form-group">
               @Html.LabelFor(m => m.Password, new { @class = "col-md-2 control-label" })
               <div class="col-md-10">
                  @Html.PasswordFor(m => m.Password, new { @class = "form-control" })
                  @Html.ValidationMessageFor(m => m.Password, "", new { @class = "text-danger" })
               </div>
            </div>
            <div class="form-group">
               <div class="col-md-offset-2 col-md-10">
                  <div class="checkbox">
                     @Html.CheckBoxFor(m => m.RememberMe)
                     @Html.LabelFor(m => m.RememberMe)
                  </div>
               </div>
            </div>
            <div class="form-group">
               <div class="col-md-offset-2 col-md-10">
                  <input type="submit" value="Log in" class="btn btn-default" />
               </div>
            </div>
            <p>
               @Html.ActionLink("Register as a new user", "Register")
            </p>
            @* Enable this once you have account confirmation enabled for password reset functionality *@
            <p>
               @Html.ActionLink("Forgot your password?", "ForgotPassword")
            </p>
         }
      </section>
   </div>
   <div class="col-md-4">
      <section id="socialLoginForm">
         @Html.Partial("_ExternalLoginsListPartial", new ExternalLoginListViewModel { ReturnUrl = ViewBag.ReturnUrl })
      </section>
   </div>
</div>

@section Scripts {
   @Scripts.Render("~/bundles/jqueryval")
}

Na stránce Přihlášení se teď zobrazí odkaz pro resetování hesla.

Jakmile uživatel vytvoří nový místní účet, odešle e-mailem potvrzovací odkaz, který musí použít, aby se mohl přihlásit. Pokud uživatel omylem odstraní potvrzovací e-mail nebo e-mail nikdy nedorazí, bude potřebovat potvrzovací odkaz odeslaný znovu. Následující změny kódu ukazují, jak to povolit.

Na konec souboru Controllers\AccountController.cs přidejte následující pomocnou metodu:

private async Task<string> SendEmailConfirmationTokenAsync(string userID, string subject)
{
   string code = await UserManager.GenerateEmailConfirmationTokenAsync(userID);
   var callbackUrl = Url.Action("ConfirmEmail", "Account",
      new { userId = userID, code = code }, protocol: Request.Url.Scheme);
   await UserManager.SendEmailAsync(userID, subject,
      "Please confirm your account by clicking <a href=\"" + callbackUrl + "\">here</a>");

   return callbackUrl;
}

Aktualizujte metodu Register tak, aby používala novou pomocnou rutinu:

//
// POST: /Account/Register
[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)
      {
         //  Comment the following line to prevent log in until the user is confirmed.
         //  await SignInManager.SignInAsync(user, isPersistent:false, rememberBrowser:false);

         string callbackUrl = await SendEmailConfirmationTokenAsync(user.Id, "Confirm your account");

         ViewBag.Message = "Check your email and confirm your account, you must be confirmed "
                         + "before you can log in.";

         return View("Info");
         //return RedirectToAction("Index", "Home");
      }
      AddErrors(result);
   }

   // If we got this far, something failed, redisplay form
   return View(model);
}

Aktualizujte metodu Login a znovu odešlete heslo, pokud se uživatelský účet nepotvrdil:

//
// POST: /Account/Login
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<ActionResult> Login(LoginViewModel model, string returnUrl)
{
   if (!ModelState.IsValid)
   {
      return View(model);
   }

   // Require the user to have a confirmed email before they can log on.
  // var user = await UserManager.FindByNameAsync(model.Email);
   var user =  UserManager.Find(model.Email, model.Password);
   if (user != null)
   {
      if (!await UserManager.IsEmailConfirmedAsync(user.Id))
      {
         string callbackUrl = await SendEmailConfirmationTokenAsync(user.Id, "Confirm your account-Resend");

          // Uncomment to debug locally  
          // ViewBag.Link = callbackUrl;
         ViewBag.errorMessage = "You must have a confirmed email to log on. "
                              + "The confirmation token has been resent to your email account.";
         return View("Error");
      }
   }

   // This doesn't count login failures towards account lockout
   // To enable password failures to trigger account lockout, change to shouldLockout: true
   var result = await SignInManager.PasswordSignInAsync(model.Email, model.Password, model.RememberMe, shouldLockout: false);
   switch (result)
   {
      case SignInStatus.Success:
         return RedirectToLocal(returnUrl);
      case SignInStatus.LockedOut:
         return View("Lockout");
      case SignInStatus.RequiresVerification:
         return RedirectToAction("SendCode", new { ReturnUrl = returnUrl, RememberMe = model.RememberMe });
      case SignInStatus.Failure:
      default:
         ModelState.AddModelError("", "Invalid login attempt.");
         return View(model);
   }
}

Kombinování sociálních a místních přihlašovacích účtů

Místní a sociální účty můžete kombinovat kliknutím na svůj e-mailový odkaz. V následující sekvenci RickAndMSFT@gmail.com se nejprve vytvoří jako místní přihlášení, ale účet můžete nejprve vytvořit jako přihlášení na sociální síti a pak přidat místní přihlášení.

Snímek obrazovky znázorňující domovskou stránku My A S P dot Net Log In Home Ukázka ID uživatele je zvýrazněná.

Klikněte na odkaz Spravovat . Poznámka: Externí přihlášení: 0 přidružené k tomuto účtu.

Snímek obrazovky, který zobrazuje stránku Moje A S P tečka Net Manage your account (Moje A S P tečka Net Manage your account). Vedle řádku Externí přihlášení je zvýrazněný odkaz 0 a Spravovat.

Klikněte na odkaz na jinou službu přihlášení a přijměte žádosti o aplikaci. Tyto dva účty se zkombinovaly, budete se moct přihlásit pomocí některého z těchto účtů. Můžete chtít, aby uživatelé přidávali místní účty v případě, že je služba ověřování sociálních sítí v provozu, nebo pravděpodobně ztratili přístup ke svému účtu na sociální síti.

Na následujícím obrázku je Tom sociální protokol (který můžete vidět z externích přihlášení: 1 zobrazený na stránce).

Snímek obrazovky, který zobrazuje stránku Moje A S P tečka Net Manage your account (Moje A S P tečka Net Manage your account). Zvýrazněné jsou řádky Vybrat heslo a Externí přihlášení.

Kliknutím na Vybrat heslo můžete přidat místní přihlášení přidružené ke stejnému účtu.

Snímek obrazovky znázorňující stránku My A S P dot Net Create Local Login (Vytvořit místní přihlašovací stránku) Do pole Nové heslo se zadává ukázkové heslo a potvrďte nová textová pole hesla.

Podrobnější potvrzení e-mailu

My tutorial Account Confirmation and Password Recovery with ASP.NET Identity goes into this topic with more details.

Ladění aplikace

Pokud vám nepřijdou e-maily s odkazem:

  • Zkontrolujte složku nevyžádané pošty nebo spamu.
  • Přihlaste se ke svému účtu SendGrid a klikněte na odkaz E-mailová aktivita.

Pokud chcete testovat ověřovací odkaz bez e-mailu, stáhněte si dokončenou ukázku. Na stránce se zobrazí potvrzovací odkaz a potvrzovací kódy.

Další materiály