原文出自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>();
全文完