【译】ASP.NET Identity Core 从零开始

原文出自Rui
Figueiredo的博客,原文链接《ASP.NET
Identity Core From
Scratch》

翻译注:这首博文发布时正在Asp.Net Core 1.1 时期,原博主使用的是 vs
code+ yeoman+ node.js。现在(2017年12月22日)已经到了Asp.Net Core
2.0一时了,
文中的代码有了变通,yeoman上啊找不至新型2.0之沙盘,所以于此译文中,博主对该发了转,开发工具由
vs code+yeoman+node.js 替换为vs code+dotnet cli,.net core sdk
从1.x荣升至2.x,这篇稿子多少内容和原文不一样的,但是完全思路没换。动手做本文中的例子请准备好上述原则,鉴于上述的工具和软件,你可以以windows
linux osX进行编码。如果您以windows上以visual studio 2017
开发,那么参照本章毕没问题

摘要:本文主要介绍了,在匪利用visual
studio模板的图景下,如何一步一步之将 Asp.Net Core
Identity引入到您的门类遭到,在这些手续中,你拿明了
使用Identity的必要步骤,以及借助,学习一个框架,没什么可比从0开始做相同满再好之了。

能够被用户以公的网站及创设帐户往往是创建网站的首先步。

虽这是一个关押起十分平常的职责,但它关系到众多做事,而且很易错。

虽然你得用Visual
Studio附带的沙盘(如果你不使用Windows,也足以使yeoman模板翻译注:在译文被利用的dotnet
cli
),但是模板就见面吃您一个休完全的化解方案,你要自己安装电子邮件确认。

这些模板往往会暗藏许多底底细,所以如果出现问题,而且你无了解这些细节,那么您以十分为难找到题目的发源。

即首博客是一个起零星(我们会于 empty web application
模板开始)配置Identity的step by
step
点。这篇博客实现用户创建账户,邮件确认,以及密码重置。你可以以这里找到最后的型代码。

乃可行使 git克隆这个档案库:

git clone https://github.com/ruidfigueiredo/AspNetIdentityFromScratch

本指南假早晚你利用的凡Visual Studio
Code,以便可以在Windows,Mac或Linux中操作。如果您在使Visual
Studio的完全版本,那么也是尚未问题之。

俺们借要你早就设置了.Net Core 运行时和
Sdk。

1 . 缔造一个空的Web应用程序

率先打开cmd然后倒到某某你若创造项目之目,注意目录的尾声一级是将创建项目的称号,稍后将以
dotnet cli在此间开创项目,它看起也许是这般的:

D:\IdentityFromScratch

然后在cmd中输入

 dotnet new web

取得的结果类似下面这样:

已成功创建模板“ASP.NET Core Empty”。                                                                         
此模板包含非 Microsoft 的各方的技术,有关详细信息,请参阅 https://aka.ms/template-3pn。                                      

正在处理创建后操作...                                                                                         
正在 D:\IdentityFromScratch\IdentityFromScratch.csproj 上运行 "dotnet restore"...                         
  Restoring packages for D:\IdentityFromScratch\IdentityFromScratch.csproj...                        
  Generating MSBuild file D:\IdentityFromScratch\obj\IdentityFromScratch.csproj.nuget.g.props.       
  Generating MSBuild file D:\IdentityFromScratch\obj\IdentityFromScratch.csproj.nuget.g.targets.     
  Restore completed in 3.04 sec for D:\IdentityFromScratch\IdentityFromScratch.csproj.               

还原成功。

行使vs code
打开项目所于的文本夹(在本文中凡是D:\IdentityFromScratch\),vs code
将提示
Required assets to build and debug are missing from 'IdentityFromScratch'. Add them?,vs
code 在打听你是不是丰富调试资源,选择yes,vs code将会朝目录中上加
.vscode文件夹,里面包含调试相关的布文件。

开辟Startup.cs并加上应用MVC所待的Ioc配置。

public void ConfigureServices(IServiceCollection services)
{
    services.AddMvc();
}

此外,更新Configure方法,以便将MVC中间件添加至管道,这里以默认路由。

public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
    if (env.IsDevelopment())
    {
        app.UseDeveloperExceptionPage();
    }

    app.UseStaticFiles();
    app.UseMvcWithDefaultRoute();
}

万一您不熟识ASP.NET
Core中的中间件和IoC,那么中等件文档和因注入文档克协助你飞了解她们。

以咱们使用的凡默认路由,所以一旦对咱网站的绝望目录进行http请求,那么要调用的控制器就是HomeController。控制器的action将是Index,所以让咱们创建这些不可或缺之事物。

首先在vs
code中到底目录(IdentityFromScratch)下创造一个叫吧Controllers的文本夹(空模板里没有)。

接下来创建HomeController.cs文件,
文件内容如下:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc;

namespace AspNetIdentityFromScratch.Controllers
{
    public class HomeController : Controller
    {
        public IActionResult Index()
        {
            return View();
        }
    }
}

接下来再次当跟根目录下创办Views文件夹,在Views下创造Shared文件夹。

于Shared文件夹下创办 _Layout.cshtml 文件,文件的情很粗略:

<!DOCTYPE html>

<html>
<head>
    <meta name="viewport" content="width=device-width" />
    <title>@ViewBag.Title</title>
</head>
<body>
    @RenderBody()
</body>
</html>

_Layout.cshtml
文件是一个布局文件,里面的@RenderBody()用我们的视图渲染在这里。

以MVC中,还有一个特种的文件它见面于具备View之前自动加载,它就是是
_ViewStart.cshtml,在Views文件夹下创造它,其中的代码很少,如下:

@{
    Layout = "_Layout";
} 

随即行代码将默认布局设置为我们恰好创建的布局。

另外,因为稍后咱们用采取ASP.NET Core的一个叫做吧tag
helper的新力量,所以我们将应用其他一个文本(它是文件称_ViewImports.cshtml)为具视图启用其。在Views文件夹下创造_ViewImports.cshtml,其情如下:

@addTagHelper "*, Microsoft.AspNetCore.Mvc.TagHelpers"

_ViewImports.cshtml允许我们抬高@using语句,例如@using System.Collections.Generic,以便在_ViewImports所于的文本夹和其子文件夹着之有着Razor视图中直接以ViewImprots文件被援引的命名空间下之好像设毋庸添加@using语句。所以我们以这边丰富
tag helper之后,所有的视图中还足以以她。

我们照例需要 Index
action的视图,接下当Views下开创Home文件夹,然后创建Index.cshtml视图文件,文件内容如下:

<h1>Home</h1>
@if (User.Identity.IsAuthenticated)
{
    <p>User @User.Identity.Name is signed in. <a href="/Account/Logout">Logout</a> </p>
}
else
{
    <p>No user is signed in.</p>
}
<div>
    <a href="/Account/Login">Login</a>
</div>
<div>
    <a href="/Account/Register">Register</a>
</div>
<div>
    <a href="/Account/ForgotPassword">Forgot Password</a>
</div>    

运转该种,你应当看一个非常简单的页面,提醒您从未用户登录。里面里的链接也无从正常工作,因为咱们稍事晚才会处理它们。

翻译注:在vs code 中本下迅速键 Ctrl+~来启动终端,

键入 dotnet run即可运行品种,如果坏的讲话先运行dotnet build

ASP.NET Core Identity

ASP.NET Core Identity是ASP.NET
Core的成员系统,它提供了保管用户帐户所急需的效果。通过动用她,我们拿能够创立用户并生成token以用来电子邮件确认与密码重置。

公用添加底两只Nuget包:

Microsoft.AspNetCore.Identity
Microsoft.AspNetCore.Identity.EntityFrameworkCore

翻译注:当我们利用dotnet
cli创建项目是,默认添加了Microsoft.AspNetCore.All,这个保险中含有所有这篇稿子所用的管(除非自己标记了不可知忽视),所以本文中仅仅开牵线,下文同理。另外你得打开项目之.csproj文件查看和编辑项目之保证,这同1.x本子是休一样的。

以继续下同样步事先,请小心有关ASP.NET Core
Identity如何存储用户的消息的。一个用户将具有(至少)名也IdentityUser的类吃带有的享有属性。你可创造IdentityUser的子类并上加更多属性。当我们其实创造数据库时,这些以起于用户的表中。对于本文中之例证,我们将单纯下默认的
IdentityUser

因我们用一个数据库来囤积我们的用户数据,这里我们以动用SQLite(如果你闹趣味使用不同之数据库,请查看这首文章:Cross
platform database walk-through using ASP.NET MVC and Entity Framework
Core)。

Identity使用SqLite存储数据要添加Nuget包:

Microsoft.EntityFrameworkCore.Sqlite

然后打开项目之.csproj文件,在
ItemGroup中添加(翻译注:这里不可知忽视,这里添加了Ef的命令行工具):

Microsoft.EntityFrameworkCore.Tools.DotNet
Microsoft.EntityFrameworkCore.Tools

终极之代码看起是这么的:

...
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.All" Version="2.0.3" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="2.0.1" PrivateAssets="All" />
</ItemGroup>

<ItemGroup>
<DotNetCliToolReference Include="Microsoft.EntityFrameworkCore.Tools.DotNet" Version="2.0.1" />
</ItemGroup>
...

Configuring ASP.NET Core Identity

一般来讲,使用Identity
的口舌我们设你的用户类是继续给IdentityUser的。然后你可以当你的用户类中上加额外之用户信息。你应用程序的DbContex呢当继承与IdentityDbContext,之后你得当里边指定额外的DbSet

然后尴尬的事务来了,有不少理由被你转移失去这样做。首先,ASP.NET Core
Identity
不是绝无仅有可用之成员系统,甚至不同版本的Identity也在在十分十分之区别。因此,如果你一点一滴将你的用户类和DbContext与ASP.NET
Core Identity绑定,则实在以其绑定到特定版本的Identity。

被有这些干活儿无论需用您的色绑定到Identity上是平等宗额外的办事,接下我们见面举行这些事。

率先,我们要变更Startup.cs并丰富必要的IoC配置:

public void ConfigureServices(IServiceCollection services)
{
    services.AddMvc();

    services.AddDbContext<IdentityDbContext>(options =>
    {
        options.UseSqlite(
            "Data Source=users.sqlite",
            builder =>
            {
                builder.MigrationsAssembly("AspNetIdentityFromScratch");
            });
    });

    services.AddIdentity<IdentityUser, IdentityRole>()
        .AddEntityFrameworkStores<IdentityDbContext>()
        .AddDefaultTokenProviders();

    // services.AddTransient<IEmailSender, EmailSender>();

}

俺们于Ioc容器中注册了 IdentityDbContext
并使用Data Source = users.sqlite
作为连续字符串。当我们创建数据库时,会以档次之出口目录中开创一个誉为吧users.sqlite的文书(稍后会详细介绍)。

请注意UseSqlite推而广之方法中应用的老二个参数:

builder =>
{
    builder.MigrationsAssembly("IdentityFromScratch");
});

当时段代码是必要的,因为当我们运行工具(dotnet
ef)来创造数据库迁移时,将实行检查来验证IdentityDbContext类是否与汝运行工具的种类坐落同之次第集中。如果未是这般,你也许会见取得一个谬误:

Your target project 'IdentityFromScratch' doesn't match your migrations assembly 'Microsoft.AspNetCore.Identity.EntityFrameworkCore'
...

即时即是怎么咱们得采取UseSqlite的老二个参数。
IdentityFromScratch是自家用于项目之名号,你需要用那创新为你用于你色之称呼。

接下是登记Identity:

services.AddIdentity<IdentityUser, IdentityRole>()
    .AddEntityFrameworkStores<IdentityDbContext>()
    .AddDefaultTokenProviders();

此间我们指定了咱纪念使用于用户的类IdentityUser和角色IdentityRole。如前所述,你可指定你协调的IdentityUser的子类,但是倘若你如此做,还欲转移DbContext的挂号方式,例如:

services.AddDbContext<IdentityDbContext<YourCustomUserClass>>(...
services.AddIdentity<YourCustomUserClass, IdentityRole>()
        .AddEntityFrameworkStores<IdentityDbContext<YourCustomUserClass>>(...

君恐怕注意到自家尚未关系关于IdentityRole的其它工作。你可以忽略这个,因为您得加加角色claim来取代role。这里指定IdentityRole我猜这仅是盖在原有成员系统中role的定义特别突出,不过当咱们广大使用claim的今日,role已经不那么要了。另外,指定一个自定义的IdentityRole会要工作变得尤为扑朔迷离,我用通往而来得什么使用claim将role添加到你的用户。

接下来:

.AddEntityFrameworkStores<IdentityDbContext>()

就就指定使用谁DbContext,最后:

.AddDefaultTokenProviders();

TokenProvider是变化确认电子邮件与重置密码token的组件,稍后咱们将动用它们。

兹我们只需要更新管道:

public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
    //...

    app.UseIdentity();
    app.UseStaticFiles();            
    app.UseMvcWithDefaultRoute();
}

使用UseIdentity扩大方法时会见有啊也?这实则它们见面安装cookie中间件,cookie中间件做的是当MVC中之controller
action返回401响应时以用户重定向到登录页面,并且在用户登录并创建身份验证cookie后,将该转移为ClaimsPrincipal和ClaimsIdentity,也就是是在你使用Request.User时获取到之实在目标。

浮动存储用户数据的数据库

于以新本子的EF时,生成数据库基本上只能利用migrations。接下来我们用创造一个搬迁,然后被工具转数据库。

打开vscode中的顶(上文中发生关系了),输入下面的吩咐:

dotnet ef migrations add Initial

顿时将创名吧“Initial”的迁移,使用下的命令下它:

dotnet ef database update

欠令所做的是以拥有pending的搬,在本例情况下,它只是会是“Initial”,它以创设ASP.NET
Core Identity所必不可少的表。

今昔应该在输出文件夹着生出一个叫做也users.sqlite的文本。

乃当也可坐编程方式变通数据库(但你老欲首先创建迁移)。

这般,如果您想以及其他人分享你的色,他们以不用运行dotnet ef database update即能运作该型。

为落实者目标,在Startup.cs的配备方式中增长:

public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory, IdentityDbContext dbContext)
{
    if (env.IsDevelopment())
    {
        app.UseDeveloperExceptionPage();
        dbContext.Database.Migrate(); // 如果数据库不存在则会被创建
    }
    //...
}

不过你得要是当安也“development”的条件遭到运作。通常情况下,如果你从Visual
Studio运行那么即便是development,但是要你说了算由命执行运行的说话:

dotnet run --environment "Development"

发送确认邮件

为了启用电子邮件验证和密码重置,我们要被应用程序能够发送电子邮件。当您刚好处在开发阶段时,最简便易行的主意就是是拿邮件信息存到磁盘文件上。这样可以节省而于品味操作时只能待电子邮件的流年。首先我们得举行的是创造一个接口,我们以那个命名为IMessageService

public interface IMessageService
{
    Task Send(string email, string subject, string message);
}

形容邮件及文件之兑现类似:

public class FileMessageService : IMessageService
{
    Task IMessageService.Send(string email, string subject, string message)
    {
        var emailMessage = $"To: {email}\nSubject: {subject}\nMessage: {message}\n\n";

        File.AppendAllText("emails.txt", emailMessage);                          

        return Task.FromResult(0);
    }
}

最后一码事是以Startup.cs的ConfigureServices中登记之服务:

public void ConfigureServices(IServiceCollection services)
{            
    //...

    services.AddTransient<IMessageService, FileMessageService>();
}

用户注册页面

于这演示,我们将构建最简单易行的Razor视图,因为我们的目的是显得如何下ASP.NET
Core Identity。

在Controllers文件夹着创造AccountController。

于构造函数中添加UserManager

public class AccountController : Controller
{
    private readonly UserManager<IdentityUser> _userManager;
    private readonly SignInManager<IdentityUser> _signInManager;
    private readonly IMessageService _messageService;

    public AccountController(UserManager<IdentityUser> userManager, SignInManager<IdentityUser> signInManager, IMessageService messageService)
    {
        this._userManager = userManager;
        this._signInManager = signInManager;
        this._messageService = messageService;
    }
    //...

UserManager是故来创造用户以及转移验证token的切近。SignInManager
提供密码验证的效应,以及用户之登录和注销(通过拍卖authentication
cookie)。

开创注册方式:

public IActionResult Register()
{                                    
    return View();
}   

以及 Views/Account/Register.cshtml 文件:

<form method="POST">
    <div>
        <label >Email</label>
        <input type="email" name="email"/>
    </div>

    <div>
        <label>Password</label>
        <input type="password" name="password"/>
    </div>

    <div>
        <label>Retype password</label>
        <input type="password" name="repassword"/>
    </div>

    <input type="submit"/>
</form>

<div asp-validation-summary="All"></div>

值得注意的凡 asp-validation-summary tag helper,tag helper是ASP.NET
Core中之新职能。它们用来代替旧的HtmlHelpers。

validation summary tag
helper,设置的值是All,所以它用显得有模型错误。我们用运用她来显示创建用户时时检测到之另不当(例如,用户名已被占据或密码不般配)。

下是用户注册表单接收的骨子里处理措施:

[HttpPost]
public async Task<IActionResult> Register(string email, string password, string repassword)
{
    if (password != repassword)
    {
        ModelState.AddModelError(string.Empty, "两次输入的密码不一致");
        return View();
    }

    var newUser = new IdentityUser 
    {
        UserName = email,
        Email = email
    };

    var userCreationResult = await _userManager.CreateAsync(newUser, password);
    if (!userCreationResult.Succeeded)
    {
        foreach(var error in userCreationResult.Errors)
            ModelState.AddModelError(string.Empty, error.Description);
        return View();
    }

    var emailConfirmationToken = await _userManager.GenerateEmailConfirmationTokenAsync(newUser);
    var tokenVerificationUrl = Url.Action("VerifyEmail", "Account", new {id = newUser.Id, token = emailConfirmationToken}, Request.Scheme);

    await _messageService.Send(email, "验证电子邮件", $"点 <a href=\"{tokenVerificationUrl}\">我</a> 验证邮件");

    return Content("请检查你的邮箱,我们向你发送了邮件。");
} 

一经创我们的新用户,我们首先必须创造IdentityUser的实例。由于我们用email作为用户称,所以我们呢那指定了一样的价。
然后我们调用_userManager.CreateAsync(newUser,password);其的归来值备受产生一个曰也Successbool值属性,它表示了用户是否创造成功。里面的其它一个属性Errors遭含有创建失败的原由(例如:不入密码要求,用户名已被霸占等)。

此时,如果userCreatingResult.Successtrue,则用户已创造。但是,如果您只要反省newUser.EmailConfirmed它用赶回false

盖咱们只要证实电子邮件,所以我们可以动用_userManagerGenerateEmailConfirmationTokenAsync变迁一个电子邮件确认token。然后,我们用IMessagingService来“发送”它。

使您本运作品种并改变到Account/Register创建一个新用户,那么当此过程后,你的品种文件夹着应发生一个名为也emails.txt的文件,里面包含确认链接。

咱们见面创造 email confirmation controller
action,之后你不怕好复制文件中之电子邮件确认网址,然后验证电子邮件(稍后用召开这些)。

用角色添加为Claim

上文提到您莫待了多之关切IdentityRole,因为您得用角色添加为Claim,以下是怎么样拿“Administrator”角色添加到用户之演示:

await _userManager.AddClaimAsync(identityUser, new Claim(ClaimTypes.Role, "Administrator"));

接下来,你可像以前一样以controller action上要求“Administrator”角色:

[Authorize(Roles="Administrator")]
public IActionResult RequiresAdmin()
{
    return Content("OK");
}

邮件验证

让咱们来创造controller
action,当用户点击电子邮件中之链接时用会调用该操作:

public async Task<IActionResult> VerifyEmail(string id, string token)
{
    var user = await _userManager.FindByIdAsync(id);
    if(user == null)
        throw new InvalidOperationException();

    var emailConfirmationResult = await _userManager.ConfirmEmailAsync(user, token);
    if (!emailConfirmationResult.Succeeded)            
        return Content(emailConfirmationResult.Errors.Select(error => error.Description).Aggregate((allErrors, error) => allErrors += ", " + error));                            

    return Content("Email confirmed, you can now log in");
}    

咱俩得采取用户之ID来加载IdentityUser。有了这与电子邮件确认token,我们得以调用userManagerConfirmEmailAsync法。如果token是不利的,则用户数据库表中的EmailConfirmed特性将给更新为指示用户之电子邮件已认可。

由简化示例的目的,我们用了Content办法。你得创建一个初视图,然后加一个越反到登录页面的连天。有相同码事若免应做的哪怕是当肯定之后自动登录用户。
及时是为一旦用户多次点击确认链接,都非会见掀起其他错误。

若你想以肯定电子邮件后自动登录用户,则该链接本质上用变成登录的平等栽方式,而且免待使用密码,又能够屡屡以。

报到页面

第一在AccountController中开创一个Login方法来处理GET请求:

public IActionResult Login()
{
    return View();
}    

和她的视图 Views/Account/Login.cstml:

<form method="POST">
    <div>
        <label>Email</label>
        <input type="email" name="email"/>
    </div>

    <div>
        <label>Password</label>
        <input type="password" name="password"/>
    </div>

    <input type="submit"/>
</form>

<div asp-validation-summary="All"></div>

控制器处理POST请求的操作:

[HttpPost]
public async Task<IActionResult> Login(string email, string password, bool rememberMe)
{
    var user = await _userManager.FindByEmailAsync(email);
    if (user == null)
    {
        ModelState.AddModelError(string.Empty, "Invalid login");
        return View();
    }
    if (!user.EmailConfirmed)
    {
        ModelState.AddModelError(string.Empty, "Confirm your email first");
        return View();
    }

    var passwordSignInResult = await _signInManager.PasswordSignInAsync(user, password, isPersistent: rememberMe, lockoutOnFailure: false);
    if (!passwordSignInResult.Succeeded)
    {
        ModelState.AddModelError(string.Empty, "Invalid login");
        return View();                
    }

    return Redirect("~/");
}

这里我们使用UserManager经过电子邮件(FindByEmailAsync)来查找用户。如果电子邮件未给认同,我们不同意用户登录,在这种状态下而还提供再发送验证邮件的取舍项。

咱们用动用SignInManager拿用户登录。它的PasswordSignInAsync措施要IdentityUser,正确的密码,标志isPersistent及另一个表明lockoutOnFailure

是主意所做的凡提倡创建加密cookie,cookie将为发送至用户之计算机达,cookie中蕴含该用户的具有claims。成功登录后,你可使用Chrome的开发人员工具检查这个Cookie。

图片 1

isPersistent参数用控制Cookie的Expires属性的值。

倘安也true(截图中就是这种气象),cookie的超时日期会是几个月下(此日范围是可部署的,请参考配置有)。当装为false时,cookie将Expires属性设置为session,这意味着当用户关闭浏览器后cookie将于删除。

lockoutOnFailure表明将当用户多次登录失败以后阻止用户登录。用户为锁定多少坏和多长时间是可配置的(我们拿当部署部分涉及这或多或少)。如果您控制使lockoutOnFailure,请留心,每次用户登录失败时还用调用_userManager.AccessFailedAsync(user)。

自家不经意了同起事情,不过其杀粗略,那即便是returnUrl。如果您想,你得增长一个参数到Login方法,这样当用户成功登录时,你可以发送一个重定向到return
url。不过你应有检查url是否是local的(即连续是依赖于你的应用程序,而非是他人的),为者,在controller
action中好尽以下操作:

if (Url.IsLocalUrl(returnUrl))
{
    return Redirect(returnUrl);
}else
{
    return Redirect("~/");                    
}

密码重置

对于密码重置,我们要一个页面。
第一controller action,我们命名也ForgotPassword

public IActionResult ForgotPassword()
{
    return View();
}

视图 /Views/Account/ForgotPassword.cshtml:

<form method="POST">
    <div>
        <label>Email</label>
        <input type="email" name="email"/>
    </div>

    <input type="submit"/>
</form>

处理Post请求的艺术:

[HttpPost]
public async Task<IActionResult> ForgotPassword(string email)
{
    var user = await _userManager.FindByEmailAsync(email);
    if (user == null)
        return Content("Check your email for a password reset link");

    var passwordResetToken = await _userManager.GeneratePasswordResetTokenAsync(user);
    var passwordResetUrl = Url.Action("ResetPassword", "Account", new {id = user.Id, token = passwordResetToken}, Request.Scheme);

    await _messageService.Send(email, "Password reset", $"Click <a href=\"" + passwordResetUrl + "\">here</a> to reset your password");

    return Content("Check your email for a password reset link");
}

乃恐怕会见专注到,无论电子邮件是否属于一个设有的用户,用户都将看到同一的消息。
你当这么做,因为一旦你切莫这么做,则可采取是作用来发现用户是否在您的站点中具备帐户。

控制器的其余部分只是生成token并发送电子邮件。

我们还需要构建ResetPasswordcontroller
action来处理我们用电子邮件发送给用户的链接。

[HttpPost]
public async Task<IActionResult> ResetPassword(string id, string token, string password, string repassword)
{           
    var user = await _userManager.FindByIdAsync(id);
    if (user == null)
        throw new InvalidOperationException();

    if (password != repassword)
    {
        ModelState.AddModelError(string.Empty, "Passwords do not match");
        return View();
    }

    var resetPasswordResult = await _userManager.ResetPasswordAsync(user, token, password);
    if (!resetPasswordResult.Succeeded)
    {
        foreach(var error in resetPasswordResult.Errors)
            ModelState.AddModelError(string.Empty, error.Description);
        return View();                
    }

    return Content("Password updated");
}

倘你想了解干什么我并未于表单中补充加token和用户ID作为隐藏字段,这是以MVC
model binder会在url参数中找到她。不过多数示范(以及来Visual
Studio的个人用户帐户的默认模板)都用其上加为隐藏字段。

这个action没有最好多内容,只是以_userManager.ResetPasswordAsync否用户安装一个初的密码。

取消登陆

注销一个用户只有待运用SignInManager并调用SignOutAsync

[HttpPost]
public async Task<IActionResult> Logout()
{
    await _signInManager.SignOutAsync();
    return Redirect("~/");
}

无非允许用户使用Http Post
方式收回绝对是只再次好之艺术。

吁留心,为了简单起见,我在示范中没这样做。所以要是您照在开的言辞请不要添加[HttpPost](Index页面有链接到/Account/Logout),或者用Index.cshtml中之取消链接更改为发送到/Account/Logout的表单。

配置

君可能想清楚,如果自己选择与AccountController不同的名称,所有这些代码还能办事为?也许你会将登录action命名也SignIn而无是Login。

一经您这么做是杯水车薪的。例如,如果你当controller
action中行使[Authorize]特性,并且用户导航及该操作的URL,则重定向拿发送到
/Account/Login。

那您怎么能够改变设置为?答案就是是于Startup.cs的ConfigureServices中注册Identity服务的时刻指定安排。

以下是什么样转默认的报到页面:

services.ConfigureApplicationCookie(o =>
{
    o.LoginPath = "/Account/SignIn";
});

安装密码要求,例如:

services.AddIdentity<IdentityUser, IdentityRole>(
    options =>
    {
        options.Password.RequireDigit = false;
        options.Password.RequiredLength = 6;
        options.Password.RequireLowercase = false;
        options.Password.RequireNonAlphanumeric = false;
        options.Password.RequireUppercase = false;
    }
    )

马上将装密码限制为极少6个字符。

您还好要求确认的电子邮件:

services.Configure<IdentityOptions>(options => {
    options.SignIn.RequireConfirmedEmail = true;               
});

万一立刻只了要求电子邮件确认,那么您将不用检查IdentityUserEmailConfirmed特性。当你尝试利用SignInManager针对用户进行登录时,会报到失败,并且结果将涵盖名为吧IsNotAllowed的性质,而且她的价为true

还发另外安排选可供使用,例如帐户被锁定之前允许的品尝次数与让锁定多长时间。

运 SendGrid 真正的出殡邮件

如若您真想发送电子邮件,你可以SendGrid。这是一个电子邮件服务,他们出一个免费的Plan,所以若可试试看。

汝得安装她的nuget包(翻译注:这个啊是无可知忽视的):

SendGrid.NetCore

以下是动SendGrid的IMessageService的实现:

public class SendGridMessageService : IMessageService
{
    public async Task Send(string email, string subject, string message)
    {
        var emailMessage = new SendGrid.SendGridMessage();
        emailMessage.AddTo(email);
        emailMessage.Subject = subject;
        emailMessage.From = new System.Net.Mail.MailAddress("senderEmailAddressHere@senderDomainHere", "info");
        emailMessage.Html = message;
        emailMessage.Text = message;

        var transportWeb = new SendGrid.Web("PUT YOUR SENDGRID KEY HERE");
        try{
            await transportWeb.DeliverAsync(emailMessage);
        }catch(InvalidApiRequestException ex){
            System.Diagnostics.Debug.WriteLine(ex.Errors.ToList().Aggregate((allErrors, error) => allErrors += ", " + error));
        }                        
    }
}

然后更新ConfigureService

services.AddTransient<IMessageService, SendGridMessageService>();

全文完

网站地图xml地图