1. 程式人生 > >要求在ASP.NET Core 2.2中確認電子郵件——第1部分

要求在ASP.NET Core 2.2中確認電子郵件——第1部分

目錄

介紹

使用程式碼

先決條件

第1步——建立Web應用程式

第2步——初始化資料庫

第3步——搭建Identity

第4步——替換預設的EmailSender

第5步——需要確認和唯一的電子郵件

第6步——為UserName新增登入名

第7步——新增未確認的電子郵件頁面

第8步——修改登入

第9步——修改確認電子郵件


在新的ASP.NET Core 2.2 Razor頁面模板中搭建和修改Identity

介紹

ASP.NET Core 2.2 Web應用程式2部分中的第1部分,用於更新已確認的電子郵件。以下是搭建和修改

Identity以在登入前要求確認電子郵件的步驟。

使用程式碼

先決條件

  • .NET Core 2.2 SDK
  • 以下VS中的一個:
    • Visual Studio版本201715.9或更高版本
    • Visual Studio for Mac 7.7或更高版本
    • Visual Studio Code C#擴充套件版本1.17.1或更高版本

您可以下載VS 2017專案或按照以下步驟修改您自己的專案。

1步——建立Web應用程式

建立一個新的VS 2017專案。

New Project

建立新的ASP.NET Core Web應用程式並將身份驗證更改為單個使用者帳戶。

New Web App

單擊確定。

2步——初始化資料庫

該專案使用SQL Server Express。(您也可要選擇自己電腦上安裝的資料庫)

編輯appsettings.json > ConnectionStringsDefaultConnection設定資料庫。

VS 2017中的程式包管理器控制檯執行命令Update-Database

3步——搭建Identity

右鍵單擊專案名稱>新增>新建構建項。

New Scaffolded Item

在左側選單中選擇Identity

Add Scaffolded Identity

單擊新增。

選中覆蓋所有檔案並選擇ApplicationDbContext

Override Identity

單擊新增。

4步——替換預設的EmailSender

編輯appsettings.json,使用您的電子郵件伺服器設定新增EmailSettings

"EmailSettings": {
    "MailServer": "smtp.some_server.com",
    "MailPort": 587,
    "SenderName": "some name",
    "Sender": "[email protected]_server.com",
    "Password": "some_password"
}

將名為Entities新資料夾新增到專案中。

Entities中新增新命名的類EmailSettings

public class EmailSettings
{
    public string MailServer { get; set; }
    public int MailPort { get; set; }
    public string SenderName { get; set; }
    public string Sender { get; set; }
    public string Password { get; set; }
}

將名為Services新資料夾新增到專案中。

Services新增命名的新類EmailSender

public interface IEmailSender
{
    Task SendEmailAsync(string email, string subject, string htmlMessage);
}

public class EmailSender : IEmailSender
{
    private readonly EmailSettings _emailSettings;

    public EmailSender(IOptions<emailsettings> emailSettings)
    {
        _emailSettings = emailSettings.Value;
    }

    public Task SendEmailAsync(string email, string subject, string message)
    {
        try
        {
            // Credentials
            var credentials = new NetworkCredential(_emailSettings.Sender, _emailSettings.Password);

            // Mail message
            var mail = new MailMessage()
            {
                From = new MailAddress(_emailSettings.Sender, _emailSettings.SenderName),
                Subject = subject,
                Body = message,
                IsBodyHtml = true
            };

            mail.To.Add(new MailAddress(email));

            // Smtp client
            var client = new SmtpClient()
            {
                Port = _emailSettings.MailPort,
                DeliveryMethod = SmtpDeliveryMethod.Network,
                UseDefaultCredentials = false,
                Host = _emailSettings.MailServer,
                EnableSsl = true,
                Credentials = credentials
            };

            // Send it...         
            client.Send(mail);
        }
        catch (Exception ex)
        {
            // TODO: handle exception
            throw new InvalidOperationException(ex.Message);
        }

        return Task.CompletedTask;
    }
}

將名稱空間新增到EmailSender.cs

using Microsoft.Extensions.Options;
using <YourProjectName>.Entities;
using System.Net;
using System.Net.Mail;

編輯Startup.cs > ConfigureServices,新增EmailSettings選項:

services.AddOptions();
services.Configure<EmailSettings>(Configuration.GetSection("EmailSettings"));

新增到Startup.cs >ConfigureServices的底部:

services.AddSingleton<IEmailSender, EmailSender>();

將名稱空間新增到Startup.cs

using <YourProjectName>.Entities;
using <YourProjectName>.Services;

編輯Register.cshtml.csForgotPassword.cshtml.csManage|Index.cshtml.cs使用新EmailSendernamespace

//using Microsoft.AspNetCore.Identity.UI.Services;
using <YourProjectName>.Services;

5步——需要確認和唯一的電子郵件

使用AddIdentity<IdentityUser, IdentityRole>而不是AddDefaultIdentity<IdentityUser>編輯Startup.cs > ConfigureServices

//services.AddDefaultIdentity<IdentityUser>()
services.AddIdentity<IdentityUser, IdentityRole>(config =>
    {
        config.SignIn.RequireConfirmedEmail = true;
        config.User.RequireUniqueEmail = true;
    })
    .AddDefaultUI(UIFramework.Bootstrap4)
    .AddEntityFrameworkStores<ApplicationDbContext>();
    .AddDefaultTokenProviders();

services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2)
    .AddRazorPagesOptions(options =>
    {
        options.AllowAreas = true;
        options.Conventions.AuthorizeAreaFolder("Identity", "/Account/Manage");
        options.Conventions.AuthorizeAreaPage("Identity", "/Account/Logout");
    });

services.ConfigureApplicationCookie(options =>
{
    options.LoginPath = $"/Identity/Account/Login";
    options.LogoutPath = $"/Identity/Account/Logout";
    options.AccessDeniedPath = $"/Identity/Account/AccessDenied";
});

Areas\Identity\Pages\Account新增命名為CheckEmailrazer頁面:

Add New Item

然後:

New Razor Page

編輯CheckEmail.cshtml

@page
@model CheckEmailModel
@{
    ViewData["Title"] = "Check email";
}

<h2>@ViewData["Title"]</h2>
<p>
    Please check your inbox to confirm your account.
</p>

編輯CheckEmail.cshtml.cs,新增AllowAnonymous裝飾:

[AllowAnonymous]
public class CheckEmailModel : PageModel
{
    public void OnGet()
    {
    }
}

將名稱空間新增到CheckEmail.cshtml.cs

using Microsoft.AspNetCore.Authorization;

編輯Register.cshtml.cs > OnPostAsync

//await _signInManager.SignInAsync(user, isPersistent: false);
//return LocalRedirect(returnUrl);
return RedirectToPage("./CheckEmail");

6步——為UserName新增登入名

編輯Areas\Identity\Pages\Account\Register.cshtml.cs,將UserName屬性新增到Inputmodel

[Required]
[StringLength(100, ErrorMessage = "The {0} must be at least {2} and 
                                   at max {1} characters long.", MinimumLength = 6)]
[Display(Name = "Login Name")]
public string UserName { get; set; }

編輯Register.cshtml,新增UserName輸入:

<div class="form-group">
    <label asp-for="Input.UserName"></label>
    <input asp-for="Input.UserName" class="form-control" />
    <span asp-validation-for="Input.UserName" class="text-danger"></span>
</div<

編輯Register.cshtml.cs > OnPostAsync,在新的IdentityUser建構函式中使用Input.UserName

var user = new IdentityUser { UserName = Input.UserName, Email = Input.Email };

編輯Login.cshtml.cs > InputModel,替換EmailUserName

public class InputModel
{
    [Required]
    [Display(Name = "Login Name")]
    public string UserName { get; set; }

    [Required]
    [DataType(DataType.Password)]
    public string Password { get; set; }

    [Display(Name = "Remember me?")]
    public bool RememberMe { get; set; }
}

編輯Login.cshtml.cs > OnPostAsync,替換Input.EmailInput.UserName

var result = await _signInManager.PasswordSignInAsync
  (Input.UserName, Input.Password, Input.RememberMe, lockoutOnFailure: true);

編輯Login.cshtml,在asp-for上使用UserName更換Email

<div class="form-group">
    <label asp-for="Input.UserName"></label>
    <input asp-for="Input.UserName" class="form-control" />
    <span asp-validation-for="Input.UserName" class="text-danger"></span>
</div<

7步——新增未確認的電子郵件頁面

Areas\Identity\Pages\Account新增命名為UnconfirmedEmailrazer頁面:

編輯UnconfirmedEmail.cshtml

@page "{userId}"
@model UnconfirmedEmailModel
@{
    ViewData["Title"] = "Confirm your email.";
}

<h2>@ViewData["Title"]</h2>
<h4>Enter your email.</h4>
<hr />

<div class="row">
    <div class="col-md-4">
        <form method="post">
            <div asp-validation-summary="All" class="text-danger"></div>
            <div class="form-group">
                <label asp-for="Input.Email"></label>
                <input asp-for="Input.Email" class="form-control" />
                <span asp-validation-for="Input.Email" class="text-danger"></span>
            </div>
            <button type="submit" class="btn btn-primary">Submit</button>
        </form>
    </div>
</div>	

@section Scripts {
    <partial name="_ValidationScriptsPartial" />
}

編輯UnconfirmedEmail.cshtml.cs

using <YourProjectName>.Services;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Text.Encodings.Web;
using System.Threading.Tasks;

namespace <YourProjectName>.Areas.Identity.Pages.Account
{
    [AllowAnonymous]
    public class UnconfirmedEmailModel : PageModel
    {
        private readonly UserManager<IdentityUser> _userManager;
        private readonly IEmailSender _emailSender;

        public UnconfirmedEmailModel(UserManager<IdentityUser> userManager, IEmailSender emailSender)
        {
            _userManager = userManager;
            _emailSender = emailSender;
        }

        [TempData]
        public string UserId { get; set; }

        [BindProperty(SupportsGet = true)]
        public InputModel Input { get; set; }

        public class InputModel
        {
            [Required]
            [EmailAddress]
            public string Email { get; set; }
        }

        public async Task OnGetAsync(string userId)
        {
            UserId = userId;
            var user = await _userManager.FindByIdAsync(userId);
            Input.Email = user.Email;
            ModelState.Clear();
        }

        public async Task<IActionResult> OnPostAsync()
        {
            if (ModelState.IsValid)
            {
                var user = await _userManager.FindByIdAsync(UserId);

                if (user == null)
                {
                    // Don't reveal that the user does not exist
                    return RedirectToPage("./CheckEmail");
                }

                if (user.Email != Input.Email)
                {
                    var errors = new List<IdentityError>();
                    if (_userManager.Options.User.RequireUniqueEmail)
                    {
                        var owner = await _userManager.FindByEmailAsync(Input.Email);
                        if (owner != null && !string.Equals
                           (await _userManager.GetUserIdAsync(owner), 
                            await _userManager.GetUserIdAsync(user)))
                        {
                            ModelState.AddModelError(string.Empty, 
                            new IdentityErrorDescriber().DuplicateEmail(Input.Email).Description);
                            return Page();
                        }
                    }

                    await _userManager.SetEmailAsync(user, Input.Email);
                }
                
                var result = await _userManager.UpdateSecurityStampAsync(user);
                if (!result.Succeeded)
                {
                    foreach (var error in result.Errors)
                    {
                        ModelState.AddModelError(string.Empty, error.Description);
                        return Page();
                    }
                }
                
                var code = await _userManager.GenerateEmailConfirmationTokenAsync(user);
                
                var callbackUrl = Url.Page(
                    "/Account/ConfirmEmail",
                    pageHandler: null,
                    values: new { userId = user.Id, code = code },
                    protocol: Request.Scheme);

                await _emailSender.SendEmailAsync(Input.Email, "Confirm your email",
                    $"Please confirm your account by 
                    <a href='{HtmlEncoder.Default.Encode(callbackUrl)}'>clicking here</a>.");

                return RedirectToPage("./CheckEmail");
            }

            return Page();
        }
    }
}

8步——修改登入

注入UserManagerAreas\Identity\Pages\Account\Login.cshtml.cs

private readonly UserManager<IdentityUser> _userManager;
private readonly SignInManager<IdentityUser> _signInManager;
private readonly ILogger<LoginModel> _logger;

public LoginModel(
    UserManager<IdentityUser> userManager,
    SignInManager<IdentityUser> signInManager,
    ILogger<LoginModel> logger)
{
    _userManager = userManager;
    _signInManager = signInManager;
    _logger = logger;
}

Login.cshtml.cs新增ShowResendUserId屬性:

public bool ShowResend { get; set; }
public string UserId { get; set; }

新增到Login.cshtml.cs > OnPostAsyncresult.IsLockedOut之後:

if (result.IsNotAllowed)
{
    _logger.LogWarning("User email is not confirmed.");
    ModelState.AddModelError(string.Empty, "Email is not confirmed.");
    var user = await _userManager.FindByNameAsync(Input.UserName);
    UserId = user.Id;
    ShowResend = true;
    return Page();
}

編輯Login.cshtml,在asp-validation-summary之後:

@{
    if (Model.ShowResend)
    {
        <p>
            <a asp-page="./UnconfirmedEmail" 
            asp-route-userId="@Model.UserId">Resend verification?</a>
        </p>
    }
}

9步——修改確認電子郵件

ShowInvalid屬性新增到Areas\Identity\Pages\Account\ConfirmEmail.cshtml.cs

public bool ShowInvalid { get; set; }

編輯ConfirmEmail.cshtml.cs > OnGetAsync

if (!result.Succeeded)
{
    //throw new InvalidOperationException($"Error confirming email for user with ID '{userId}':");
    foreach (var error in result.Errors)
    {
        ModelState.AddModelError(string.Empty, error.Description);
    }
    ShowInvalid = true;
}

編輯ConfirmEmail.cshtml

<div asp-validation-summary="All" class="text-danger"></div>
@{
    if (Model.ShowInvalid)
    {
        <p>
            Error confirming your email.
        </p>
        <p>
            If you can login, try updating your email again.<br />
            If you cannot login, try resend verification.
        </p>
    }
    else
    {
        <p>
            Thank you for confirming your email.
        </p>
    }
}

構建並測試專案。

繼續檢視第二部分。 

 

原文地址:https://www.codeproject.com/Articles/1272172/Require-Confirmed-Email-in-ASP-NET-Core-2-2-Part-1