Oturum açma, e-posta onayı ve parola sıfırlama özellikli, güvenli bir ASP.NET MVC 5 web uygulaması oluşturma (C#)

tarafından Rick Anderson

Bu öğreticide, ASP.NET Kimlik üyelik sistemini kullanarak e-posta onayı ve parola sıfırlama ile bir ASP.NET MVC 5 web uygulaması oluşturma işlemi gösterilmektedir.

Bu öğreticinin .NET Core kullanan güncelleştirilmiş bir sürümü için bkz . ASP.NET Core'da hesap onayı ve parola kurtarma.

ASP.NET MVC uygulaması oluşturma

Web veya Visual Studio 2013 için Visual Studio Express 2013'i yükleyip çalıştırarak başlayın. Visual Studio 2013 Güncelleştirme 3 veya üzerini yükleyin.

Not

Uyarı: Bu öğreticiyi tamamlamak için Visual Studio 2013 Güncelleştirme 3 veya üzerini yüklemeniz gerekir.

  1. Yeni bir ASP.NET Web projesi oluşturun ve MVC şablonunu seçin. Web Forms, ASP.NET Kimliğini de desteklediği için bir web formları uygulamasında benzer adımları izleyebilirsiniz.
    Yeni A S P nokta Net Proje sayfasını gösteren ekran görüntüsü. M V C şablonu seçilir ve Bireysel Kullanıcı Hesapları vurgulanır.

  2. Varsayılan kimlik doğrulamasını Tek Tek Kullanıcı Hesapları olarak bırakın. Uygulamayı Azure'da barındırmak istiyorsanız onay kutusunu işaretli bırakın. Öğreticinin ilerleyen bölümlerinde Azure'a dağıtacağız. Bir Azure hesabını ücretsiz olarak açabilirsiniz.

  3. Projeyi SSL kullanacak şekilde ayarlayın.

  4. Uygulamayı çalıştırın, Kaydet bağlantısına tıklayın ve bir kullanıcı kaydedin. Bu noktada, e-postadaki tek doğrulama [EmailAddress] özniteliğiyle yapılır.

  5. Sunucu Gezgini'nde Veri Bağlantıları\DefaultConnection\Tables\AspNetUsers'a gidin, sağ tıklayın ve Tablo tanımını aç'ı seçin.

    Aşağıdaki görüntüde şema gösterilmektedir AspNetUsers :

    Sunucu Gezgini'nde A S P Net Kullanıcıları Betik Dosyası sekmesini gösteren ekran görüntüsü.

  6. AspNetUsers tablosuna sağ tıklayın ve Tablo Verilerini Göster'i seçin.
    A S P Net Kullanıcıları şemasını gösteren ekran görüntüsü. Yanlış olarak etiketlenmiş E-posta Onaylandı sütunu vurgulanır.
    Bu noktada e-posta onaylanmamıştır.

  7. Satıra tıklayın ve sil'i seçin. Sonraki adımda bu e-postayı yeniden ekleyeceksiniz ve bir onay e-postası göndereceksiniz.

E-posta onayı

Başka birinin kimliğine bürünmediklerini (başka birinin e-postasına kaydolmadığını) doğrulamak için yeni bir kullanıcı kaydının e-postasını onaylamak en iyi yöntemdir. Bir tartışma forumun olduğunu varsayalım, olarak "joe@contoso.com"kaydolmasını engellemek "bob@example.com" istersiniz. E-posta onayı olmadan, "joe@contoso.com" uygulamanızdan istenmeyen e-postalar alabilirsiniz. Bob'un yanlışlıkla olarak "bib@example.com" kaydolduğunu ve bunu fark etmediğini varsayalım; uygulamada doğru e-posta adresi olmadığından parola kurtarmayı kullanamayacaktır. E-posta onayı, botlara karşı yalnızca sınırlı koruma sağlar ve belirlenen spam gönderenlere karşı koruma sağlamaz, kaydolmak için kullanabilecekleri birçok çalışma e-posta diğer adına sahiptir.

Genellikle yeni kullanıcıların e-posta, SMS kısa mesajı veya başka bir mekanizmayla onaylanana kadar web sitenize herhangi bir veri göndermesini engellemek istersiniz. Aşağıdaki bölümlerde, e-posta onayını etkinleştirecek ve yeni kaydedilen kullanıcıların e-postaları onaylanana kadar oturum açmalarını önlemek için kodu değiştireceğiz.

SendGrid'i bağlama

Bu bölümdeki yönergeler geçerli değil. Güncelleştirilmiş yönergeler için bkz . SendGrid e-posta sağlayıcısını yapılandırma.

Bu öğretici yalnızca SendGrid aracılığıyla e-posta bildirimi eklemeyi gösterse de, SMTP ve diğer mekanizmaları kullanarak e-posta gönderebilirsiniz (ek kaynaklara bakın).

  1. Paket Yöneticisi Konsolu'nda aşağıdaki komutu girin:

    Install-Package SendGrid
    
  2. Azure SendGrid kayıt sayfasına gidin ve ücretsiz bir SendGrid hesabına kaydolun. App_Start/IdentityConfig.cs aşağıdakine benzer bir kod ekleyerek SendGrid'i yapılandırın:

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

Aşağıdakileri eklemeniz gerekir:

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

Bu örneği basit tutmak için uygulama ayarlarını web.config dosyasında depolayacağız:

</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>

Uyarı

Güvenlik : Hassas verileri hiçbir zaman kaynak kodunuzda depolamayın. Hesap ve kimlik bilgileri appSetting içinde depolanır. Azure'da, bu değerleri Azure portalındaki Yapılandır sekmesinde güvenli bir şekilde depolayabilirsiniz. Bkz. Parolaları ve diğer hassas verileri ASP.NET ve Azure'a dağıtmak için en iyi yöntemler.

Hesap denetleyicisinde e-posta onayını etkinleştirme

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

Views\Account\ConfirmEmail.cshtml dosyasının doğru razor söz dizimine sahip olduğunu doğrulayın. ( İlk satırdaki @ karakteri eksik olabilir. )

@{
    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>

Uygulamayı çalıştırın ve Kaydet bağlantısına tıklayın. Kayıt formunu gönderdikten sonra oturum açmışsınızdır.

Girişte A S P nokta net günlüğüm sayfasını gösteren ekran görüntüsü.

E-posta hesabınızı kontrol edin ve e-postanızı onaylamak için bağlantıya tıklayın.

Oturum açmadan önce e-posta onayı gerektir

Şu anda bir kullanıcı kayıt formunu tamamladıktan sonra oturum açar. Oturum açmadan önce genellikle e-postalarını onaylamak istersiniz. Aşağıdaki bölümde kodu, yeni kullanıcıların oturum açmadan önce onaylanan bir e-postaya sahip olmasını gerektirecek şekilde değiştireceğiz (kimliği doğrulandı). HttpPost Register yöntemini aşağıdaki vurgulanmış değişikliklerle güncelleştirin:

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

Yöntemi açıklama satırı SignInAsync yaparak, kullanıcı kayıt tarafından oturum açmaz. Satır, TempData["ViewBagLink"] = callbackUrl; uygulamada hata ayıklamak ve e-posta göndermeden kaydı test etmek için kullanılabilir. ViewBag.Message onay yönergelerini görüntülemek için kullanılır. İndirme örneği , e-postayı ayarlamadan e-posta onayını test etmek için kod içerir ve uygulamada hata ayıklamak için de kullanılabilir.

Bir Views\Shared\Info.cshtml dosya oluşturun ve aşağıdaki razor işaretlemesini ekleyin:

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

Authorize özniteliğini Giriş denetleyicisinin Contact eylem yöntemine ekleyin. Anonim kullanıcıların erişimi olmadığını ve kimliği doğrulanmış kullanıcıların erişimi olduğunu doğrulamak için Kişi bağlantısına tıklayabilirsiniz.

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

   return View();
}

Eylem yöntemini de güncelleştirmeniz HttpPost Login gerekir:

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

Views\Shared\Error.cshtml görünümünü güncelleştirerek hata iletisini görüntüleyin:

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

AspNetUsers tablosunda test etmek istediğiniz e-posta diğer adını içeren tüm hesapları silin. Uygulamayı çalıştırın ve e-posta adresinizi onaylayana kadar oturum açamazsınız. E-posta adresinizi onayladıktan sonra Kişi bağlantısına tıklayın.

Parola kurtarma/sıfırlama

Hesap denetleyicisindeki HttpPost ForgotPassword eylem yönteminden açıklama karakterlerini kaldırın:

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

Views\Account\Login.cshtml razor görünüm dosyasındaki ActionLink'ten ForgotPassword açıklama karakterlerini kaldırın:

@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")
}

Oturum aç sayfasında artık parolayı sıfırlamak için bir bağlantı gösterilir.

Kullanıcı yeni bir yerel hesap oluşturduğunda, oturum açabilmesi için önce kullanması gereken bir onay bağlantısı e-postayla gönderilir. Kullanıcı yanlışlıkla onay e-postasını silerse veya e-posta hiç gelmezse, onay bağlantısının yeniden gönderilmesi gerekir. Aşağıdaki kod değişiklikleri bunun nasıl etkinleştirileceği gösterilmektedir.

Controllers\AccountController.cs dosyasının altına aşağıdaki yardımcı yöntemi ekleyin:

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

Register yöntemini yeni yardımcıyı kullanacak şekilde güncelleştirin:

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

Kullanıcı hesabı onaylanmamışsa parolayı yeniden göndermek için Login yöntemini güncelleştirin:

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

Sosyal ve yerel oturum açma hesaplarını birleştirme

E-posta bağlantınıza tıklayarak yerel ve sosyal hesapları birleştirebilirsiniz. Aşağıdaki sırayla RickAndMSFT@gmail.com ilk olarak yerel oturum açma olarak oluşturulur, ancak hesabı önce sosyal oturum açma günlüğü olarak oluşturabilir, ardından yerel oturum açma bilgileri ekleyebilirsiniz.

Giriş sayfasında A S P noktam Net Günlüğüm sayfasını gösteren ekran görüntüsü. Örnek Kullanıcı Kimliği D vurgulanır.

Yönet bağlantısına tıklayın. Bu hesapla ilişkili Dış Oturum Açma Bilgileri: 0'a dikkat edin.

A S P noktam Net Hesabınızı yönetin sayfasını gösteren ekran görüntüsü. Dış Oturum Açma bilgileri satırının yanında 0 ve Yönet bağlantısı vurgulanır.

Başka bir oturum açma hizmetinin bağlantısına tıklayın ve uygulama isteklerini kabul edin. İki hesap birleştirildi, iki hesapla da oturum açabileceksiniz. Kullanıcılarınızın kimlik doğrulama hizmetindeki sosyal günlüklerinin kapanması veya sosyal hesaplarına erişimi kaybetme olasılığı daha yüksek olması durumunda yerel hesaplar eklemesini isteyebilirsiniz.

Aşağıdaki görüntüde, Tom bir sosyal oturum açma işlemidir ( dış oturum açma bilgileri: 1 sayfada gösterilmiştir).

A S P noktam Net Hesabınızı yönetin sayfasını gösteren ekran görüntüsü. Parola seçin ve Dış Oturum Açma bilgileri satırları vurgulanır.

Parola seç'e tıklanması, aynı hesapla ilişkili yerel bir oturum açma eklemenize olanak tanır.

My A S P dot Net Create Local Login sayfasını gösteren ekran görüntüsü. Yeni parola ve Yeni parolayı onayla metin alanlarına örnek bir parola girilir.

E-posta onayı daha ayrıntılı

öğreticim Hesap Onayı ve ASP.NET Kimliği ile Parola Kurtarma bu konuya daha fazla ayrıntıyla giriyor.

Uygulamada hata ayıklama

Bağlantıyı içeren bir e-posta almazsan:

  • Gereksiz veya istenmeyen posta klasörünüzü denetleyin.
  • SendGrid hesabınızda oturum açın ve E-posta Etkinliği bağlantısına tıklayın.

Doğrulama bağlantısını e-posta olmadan test etmek için tamamlanmış örneği indirin. Onay bağlantısı ve onay kodları sayfada görüntülenir.

Ek Kaynaklar