原文出自Rui Figueiredo的博客,原文连接《ASP.NET Identity Core From Scratch》html
译者注:这篇博文发布时正值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 开发,那么参照本文章彻底没问题node
摘要:本文主要介绍了,在不使用visual studio模板的状况下,如何一步一步的将 Asp.Net Core Identity引入到你的项目中,在这些步骤中,你将知道 使用Identity的必要步骤,以及依赖,学习一个框架,没什么比从0开始作一遍更好的了。linux
可以让用户在你的网站上建立账户每每是建立网站的第一步。git
虽然这是一个看起来很平凡的任务,但它涉及到不少工做,并且很容易出错。github
虽然你可使用Visual Studio附带的模板(若是你不使用Windows,也可使用yeoman模板译者注:在译文中使用的dotnet cli),可是模板只会给你一个不完整的解决方案,你必须本身设置电子邮件确认。web
这些模板每每会隐藏许多的细节,因此若是出现问题,并且你不了解这些细节,那么你将很难找到问题的根源。sql
这篇博客是一个从零(咱们会从 empty web application 模板开始)配置Identity的step by step指导。这篇博客实现用户建立帐户,邮件确认,以及密码重置。你能够在这里找到最终的项目代码。数据库
你可使用 git克隆这个档案库:编程
git clone https://github.com/ruidfigueiredo/AspNetIdentityFromScratch
本指南假定你使用的是Visual Studio Code,以即可以在Windows,Mac或Linux中操做。若是你正在使用Visual Studio的完整版本,那么也是没问题的。windows
咱们假设你已经安装了.Net Core 运行时及 Sdk。
首先打开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的成员系统,它提供了管理用户账户所需的功能。经过使用它,咱们将可以建立用户并生成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> ...
通常来说,使用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);
它的返回值中有一个名为Success
的bool
值属性,它表明了用户是否建立成功。里面的另外一个属性Errors
中含有建立失败的缘由(例如:不符合密码要求,用户名已被占用等)。
此时,若是userCreatingResult.Success
为true
,则用户已经建立。可是,若是你要检查newUser.EmailConfirmed
它将返回false
。
由于咱们要验证电子邮件,因此咱们可使用_userManager
的GenerateEmailConfirmationTokenAsync
生成一个电子邮件确认token。而后,咱们使用IMessagingService来“发送”它。
若是你如今运行项目并转到Account/Register建立一个新用户,那么在此过程以后,你的项目文件夹中应该有一个名为emails.txt的文件,里面含有确认连接。
咱们会建立 email confirmation controller action,以后你就能够复制文件中的电子邮件确认网址,而后验证电子邮件(稍后将作这些)。
上文提到你不须要过多的关注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,咱们能够调用userManager
的ConfirmEmailAsync
方法。若是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。
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并发送电子邮件。
咱们仍然须要构建ResetPassword
controller 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; });
若是这只了要求电子邮件确认,那么你将没必要检查IdentityUser
的EmailConfirmed
属性。当你尝试使用SignInManager
对用户进行登陆时,会登陆失败,而且结果将包含名为IsNotAllowed
的属性,并且它的值为true
。
还有其余配置选项可供使用,例如账户被锁定以前容许的尝试次数以及被锁定多长时间。
若是你真的想发送电子邮件,你可使用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>();
全文完