【译】ASP.NET Identity Core 从零起首

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

翻译注:那篇博文发表时正值Asp.Net Core 1.1 年代,原博主使用的是 vs
code+ yeoman+ node.js。今后(二〇一七年七月23日)已经到了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起首做三回更好的了。

可见让用户在您的网站上成立帐户往往是制造网站的首先步。

即便如此那是1个看起来很常常的天职,但它关系到不少工作,而且很简单出错。

纵然你能够运用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)下开创2个名为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>    

运维该品种,你应有看到3个卓殊简单的页面,提示您没有用户登录。里面里的链接也无力回天符合规律办事,因为大家稍后才会处理它们。

翻译注:在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。接下来大家需求创造1个搬迁,然后让工具生成数据库。

开拓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);它的重回值中有2个名为Successbool值属性,它代表了用户是或不是创立成功。里面的另2天性质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中创设3个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。

sqlite 1

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

一经设置为true(截图中就是那种意况),cookie的过期日期会是多少个月之后(此时限是可配置的,请参阅配置部分)。当设置为false时,cookie将Expires属性设置为session,那表示在用户关闭浏览器之后cookie将被删除。

lockoutOnFailure标志将在用户多次签到失利将来阻止用户登录。用户被锁定多少次以及多久是可配置的(我们将在布置部分涉及那或多或少)。要是您控制利用lockoutOnFailure,请留意,每一回用户登录战败时都急需调用_userManager.AccessFailedAsync(user)。

小编不经意了一件业务,但是它很简短,那就是returnUrl。假如您想,你能够添加3个参数到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");
}

你大概会注意到,无论电子邮件是还是不是属于2个留存的用户,用户都将见到同样的新闻。
您应有如此做,因为一旦你不那样做,则能够选取此意义来发现用户是或不是在您的站点中拥有帐户。

控制器的其他部分只是生成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为用户安装二个新的密码。

撤废登陆

sqlite,注销一个用户只必要选择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]质量,并且用户导航到该操作的U景逸SUVL,则重定向将发送到
/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;
    }
    )

那将设置密码限制为最少多个字符。

您还足以要求肯定的电子邮件:

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

要是这只了需求电子邮件确认,那么您将不必检查IdentityUserEmailConfirmed质量。当您尝试使用SignInManager对用户展开登录时,会报到失利,并且结果将包括名为IsNotAllowed的性质,而且它的值为true

还有其余陈设选项可供使用,例如帐户被锁定在此之前允许的尝试次数以及被锁定多久。

使用 SendGrid 真正的发送邮件

假诺您真的想发送电子邮件,你能够运用SendGrid。那是2个电子邮件服务,他们有一个免费的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地图