麻豆小视频在线观看_中文黄色一级片_久久久成人精品_成片免费观看视频大全_午夜精品久久久久久久99热浪潮_成人一区二区三区四区

首頁 > 學(xué)院 > 開發(fā)設(shè)計(jì) > 正文

【ASP.NETIdentity系列教程(一)】ASP.NETIdentity入門

2019-11-14 14:27:19
字體:
供稿:網(wǎng)友

注:本文是【asp.net Identity系列教程】的第一篇。本系列教程詳細(xì)、完整、深入地介紹了微軟的ASP.NET Identity技術(shù),描述了如何運(yùn)用ASP.NET Identity實(shí)現(xiàn)應(yīng)用程序的用戶管理,以及實(shí)現(xiàn)應(yīng)用程序的認(rèn)證與授權(quán)等相關(guān)技術(shù),譯者希望本系列教程能成為掌握ASP.NET Identity技術(shù)的一份完整而有價(jià)值的資料。讀者若是能夠按照文章的描述,一邊閱讀、一邊實(shí)踐、一邊理解,定能有意想不到的巨大收獲!希望本系列博文能夠得到廣大園友的高度推薦

13 Getting Started with Identity
13 Identity入門

Identity is a new API from Microsoft to manage users in ASP.NET applications. The mainstay for user management in recent years has been ASP.NET Membership, which has suffered from design choices that were reasonable when it was introduced in 2005 but that have aged badly. The biggest limitation is that the schema used to store the data worked only with SQL Server and was difficult to extend without re-implementing a lot of PRovider classes. The schema itself was overly complex, which made it harder to implement changes than it should have been.
Identity是微軟在ASP.NET應(yīng)用程序中管理用戶的一個(gè)新的API。近年來用戶管理的基石一直是ASP.NET的Membership。Membership在2005年推出時(shí)還算是一個(gè)合理的選擇,但目前看來已經(jīng)嚴(yán)重過時(shí)了。它最大的限制是用以存儲(chǔ)數(shù)據(jù)的架構(gòu)(Database Schema)只能使用SQL Server,而且難以擴(kuò)展,除非重新實(shí)現(xiàn)大量的提供器類。其數(shù)據(jù)架構(gòu)本身也過于復(fù)雜,使之比理論上還要難以實(shí)現(xiàn)修改。

Microsoft made a couple of attempts to improve Membership prior to releasing Identity. The first was known as simple membership, which reduced the complexity of the schema and made it easier to customize user data but still needed a relational storage model. The second attempt was the ASP.NET universal providers, which I used in Chapter 10 when I set up SQL Server storage for session data. The advantage of the universal providers is that they use the Entity Framework Code First feature to automatically create the database schema, which made it possible to create databases where access to schema management tools wasn't possible, such as the Azure cloud service. But even with the improvements, the fundamental issues of depending on relational data and difficult customizations remained.
在發(fā)布Identity之前,微軟曾做過兩次改善Membership的嘗試。第一個(gè)嘗試稱為Simple Membership(簡單成員),它降低了數(shù)據(jù)庫架構(gòu)的復(fù)雜性,并使之易于定制用戶數(shù)據(jù),但仍然需要關(guān)系型存儲(chǔ)模型。第二個(gè)嘗試是ASP.NET的Universal Providers(通用提供器),第10章在為會(huì)話數(shù)據(jù)建立SQL Server存儲(chǔ)庫時(shí)曾用過它。Universal Providers的好處是,這些提供器使用了Entity Framework的Code First特性,能夠自動(dòng)地創(chuàng)建數(shù)據(jù)庫架構(gòu),使之能夠在架構(gòu)管理工具無法訪問的情況下,例如Azure云服務(wù),也能夠創(chuàng)建數(shù)據(jù)庫。但即使有了改善,其依賴于關(guān)系型數(shù)據(jù)以及難以定制等根本問題仍然存在。

To address both problems and to provide a more modern user management platform, Microsoft has replaced Membership with Identity. As you'll learn in this chapter and Chapters 14 and 15, ASP.NET Identity is flexible and extensible, but it is immature, and features that you might take for granted in a more mature system can require a surprising amount of work.
為了解決這兩個(gè)問題并提供一個(gè)更現(xiàn)代的用戶管理平臺(tái),微軟用Identity取代了Membership。正如將在本章以及第14、15章所了解到的,ASP.NET Identity靈活且可擴(kuò)展,但它仍不成熟,你在一些更成熟的系統(tǒng)中能夠獲得的特性,可能需要超常的工作量。

Microsoft has over-compensated for the inflexibility of Membership and made Identity so open and so adaptable that it can be used in just about any way—just as long as you have the time and energy to implement what you require.
微軟已經(jīng)完全彌補(bǔ)了Membership的不靈活性,使Identity十分開放和廣泛適應(yīng),幾乎能夠以任何方式進(jìn)行使用——只要你有時(shí)間有能力做出你所需要的實(shí)現(xiàn)即可。

In this chapter, I demonstrate the process of setting up ASP.NET Identity and creating a simple user administration tool that manages individual user accounts that are stored in a database.
在本章中,我會(huì)演示建立ASP.NET Identity的過程,并創(chuàng)建一個(gè)簡單的用戶管理工具,用以管理存儲(chǔ)在數(shù)據(jù)庫中的個(gè)別用戶賬號(hào)。

ASP.NET Identity supports other kinds of user accounts, such as those stored using Active Directory, but I don't describe them since they are not used that often outside corporations (where Active Directive implementations tend to be so convoluted that it would be difficult for me to provide useful general examples).
ASP.NET Identity還支持其他類型的用戶賬號(hào),例如用Active Directory(活動(dòng)目錄)存儲(chǔ)的賬號(hào),但我不會(huì)對(duì)其進(jìn)行描述,因?yàn)檫@種賬號(hào)通常不會(huì)用于公司的外部(這種場(chǎng)合的Active Directory實(shí)現(xiàn)往往很復(fù)雜,我難以提供有用的通用示例)。

In Chapter 14, I show you how to perform authentication and authorization using those user accounts, and in Chapter 15, I show you how to move beyond the basics and apply some advanced techniques. Table 13-1 summarizes this chapter.
在第14章中,我將演示如何用這些用戶賬號(hào)進(jìn)行認(rèn)證與授權(quán),第15章將演示如何進(jìn)入高級(jí)論題,運(yùn)用一些高級(jí)技術(shù)。表13-1是本章概要。

Table 13-1. Chapter Summary
表13-1. 本章概要
Problem
問題
Solution
解決方案
Listing
清單號(hào)
Install ASP.NET Identity.
安裝ASP.NET Identity
Add the NuGet packages and define a connection string and an OWIN start class in the Web.config file.
添加NuGet包,并在Web.config文件中定義一個(gè)鏈接字符串和一個(gè)OWIN啟動(dòng)類
1–4
Prepare to use ASP.NET Identity.
使用ASP.NET Identity的準(zhǔn)備
Create classes that represent the user, the user manager, the database context, and the OWIN start class.
創(chuàng)建表示用戶、用戶管理器、數(shù)據(jù)庫上下文的類,以及OWIN類
5–8
Enumerate user accounts.
枚舉用戶賬號(hào)
Use the Users property defined by the user manager class.
使用由用戶管理器類定義的Users屬性
9, 10
Create user accounts.
創(chuàng)建用戶賬號(hào)
Use the CreateAsync method defined by the user manager class.
使用由用戶管理器類定義的CreateAsync方法
11–13
Enforce a passWord policy.
強(qiáng)制口令策略
Set the PasswordValidator property defined by the user manager class, either using the built-in PasswordValidator class or using a custom derivation.
設(shè)置由用戶管理器類定義的PasswordValidator屬性,既可以使用內(nèi)建的PasswordValidator類,也可以使用自定義的派生類。
14–16
Validate new user accounts.
驗(yàn)證新的用戶賬號(hào)
Set the UserValidator property defined by the user manager class, either using the built-in UserValidator class or using a custom derivation.
設(shè)置由用戶管理器類定義的UserValidator屬性,既可以使用內(nèi)建的UserValidator類,也可以使用自定義的派生類。
17–19
Delete user accounts.
刪除用戶賬號(hào)
Use the DeleteAsync method defined by the user manager class.
使用由用戶管理器定義的DeleteAsync方法
20–22
Modify user accounts.
修改用戶賬號(hào)
Use the UpdateAsync method defined by the user manager class.
使用由用戶管理器類定義的UpdateAsync方法
23–24

13.1 Preparing the Example Project
13.1 準(zhǔn)備示例項(xiàng)目

I created a project called Users for this chapter, following the same steps I have used throughout this book. I selected the Empty template and checked the option to add the folders and references required for an MVC application. I will be using Bootstrap to style the views in this chapter, so enter the following command into the Visual Studio Package Manager Console and press Enter to download and install the NuGet package:
本章根據(jù)本書一直采用的同樣步驟創(chuàng)建了一個(gè)名稱為Users的項(xiàng)目。在創(chuàng)建過程中選擇了“Empty(空)” 模板,并在“Add the folders and references(添加文件夾和引用)”中選中了“MVC”復(fù)選框。本章將使用Bootstrap來設(shè)置視圖的樣式,因此在Visual Studio的“Package Manager Console(包管理器控制臺(tái))”中輸入以下命令,并按回車,下載并安裝這個(gè)NuGet包。

Install-Package -version 3.0.3 bootstrap

I created a Home controller to act as the focal point for the examples in this chapter. The definition of the controller is shown in Listing 13-1. I'll be using this controller to describe details of user accounts and data, and the Index action method passes a dictionary of values to the default view via the View method.
我創(chuàng)建了Home控制器,以作為本章示例的焦點(diǎn)。該控制器的定義如清單13-1所示。此控制器將用來描述用戶賬號(hào)的細(xì)節(jié)和數(shù)據(jù),Index動(dòng)作方法通過View方法給默認(rèn)視圖傳遞了一個(gè)字典值。

Listing 13-1. The Contents of the HomeController.cs File
清單13-1. HomeController.cs文件的內(nèi)容

using System.Web.Mvc;using System.Collections.Generic;
namespace Users.Controllers {
public class HomeController : Controller {
public ActionResult Index() { Dictionary<string, object> data = new Dictionary<string, object>(); data.Add("Placeholder", "Placeholder"); return View(data); } }}

I created a view by right-clicking the Index action method and selecting Add View from the pop-up menu. I set View Name to Index and set Template to Empty (without model). Unlike the examples in previous chapters, I want to use a common layout for this chapter, so I checked the Use a Layout Page option. When I clicked the Add button, Visual Studio created the Views/Shared/_Layout.cshtml and Views/Home/Index.cshtml files. Listing 13-2 shows the contents of the _Layout.cshtml file.
通過右擊Index動(dòng)作方法,并從彈出菜單選擇“Add View(添加視圖)”,我創(chuàng)建了一個(gè)視圖。將“View Name(視圖名稱)”設(shè)置為“Index”,并將“Template(模板)”設(shè)置為“空(無模型)”。與前面幾章的示例不同,本章希望使用一個(gè)通用的布局,于是選中了“Use a Layout Page(使用布局頁面)”復(fù)選框。點(diǎn)擊“Add(添加)”按鈕后,Visual Studio創(chuàng)建了Views/Shared/_Layout.cshtmlViews/Home/Index.cshtml文件。清單13-2顯示了_Layout.cshtml文件的內(nèi)容。

Listing 13-2. The Contents of the _Layout.cshtml File
清單13-2. _Layout.cshtml文件的內(nèi)容

<!DOCTYPE html><html><head>    <meta name="viewport" content="width=device-width" />    <title>@ViewBag.Title</title>    <link href="~/Content/bootstrap.min.CSS" rel="stylesheet" />    <link href="~/Content/bootstrap-theme.min.css" rel="stylesheet" />    <style>        .container { padding-top: 10px; }        .validation-summary-errors { color: #f00; }    </style></head><body class="container">    <div class="container">        @RenderBody()    </div></body></html>

Listing 13-3 shows the contents of the Index.cshtml file.
清單13-3顯示了Index.cshtml文件的內(nèi)容。

Listing 13-3. The Contents of the Index.cshtml File
清單13-3. Index.cshtml文件的內(nèi)容

@{    ViewBag.Title = "Index";}
<div class="panel panel-primary"> <div class="panel-heading">User Details</div> <table class="table table-striped"> @foreach (string key in Model.Keys) { <tr> <th>@key</th> <td>@Model[key]</td> </tr> } </table></div>

To test that the example application is working, select Start Debugging from the Visual Studio Debug menu and navigate to the /Home/Index URL. You should see the result illustrated by Figure 13-1.
為了測(cè)試該應(yīng)用程序示例能夠工作,從Visual Studio的“Debug(調(diào)試)”菜單中選擇“Start Debugging(啟動(dòng)調(diào)試)”,并導(dǎo)航到/Home/Index網(wǎng)址,便可以看到如圖13-1所示的結(jié)果。

圖13-1

Figure 13-1. Testing the example application
圖13-1. 測(cè)試示例應(yīng)用程序

13.2 Setting Up ASP.NET Identity
13.2 建立ASP.NET Identity

For most ASP.NET developers, Identity will be the first exposure to the Open Web Interface for .NET (OWIN). OWIN is an abstraction layer that isolates a web application from the environment that hosts it. The idea is that the abstraction will allow for greater innovation in the ASP.NET technology stack, more flexibility in the environments that can host ASP.NET applications, and a lighter-weight server infrastructure.
對(duì)于大多數(shù)ASP.NET開發(fā)者而言,Identity將是第一個(gè)暴露給OWIN(Open Web Interface for .NET——.NET開放Web接口)的組件。OWIN是一個(gè)將Web應(yīng)用程序從托管它的環(huán)境中獨(dú)立出來的抽象層。其思想是這個(gè)獨(dú)立出來的抽象層能夠使ASP.NET技術(shù)堆棧有更大的創(chuàng)新,使托管ASP.NET應(yīng)用程序的環(huán)境有更多的靈活性,并可以是輕量級(jí)的服務(wù)器架構(gòu)。

OWIN is an open standard (which you can read at http://owin.org/spec/owin-1.0.0.html). Microsoft has created Project Katana, its implementation of the OWIN standard and a set of components that provide the functionality that web applications require. The attraction to Microsoft is that OWIN/Katana isolates the ASP.NET technology stack from the rest of the .NET Framework, which allows a greater rate of change.
OWIN是一個(gè)開放標(biāo)準(zhǔn)(參閱http://owin.org/spec/owin-1.0.html)。微軟已經(jīng)創(chuàng)建了Katana項(xiàng)目,該項(xiàng)目是OWIN標(biāo)準(zhǔn)的實(shí)現(xiàn),并提供了一組提供了Web應(yīng)用程序所需功能的組件。讓微軟感興趣的是OWIN/Katana將ASP.NET技術(shù)堆棧從.NET框架的其余部分獨(dú)立了出來,這帶來了更大的修改速率(譯者對(duì)OWIN還沒太了解,這可能是指若按照OWIN的方式開發(fā)應(yīng)用程序,可以使應(yīng)用程序更易于修改——譯者注)。

OWIN developers select the services that they require for their application, rather than consuming an entire platform as happens now with ASP.NET. Individual services—known as middleware in the OWIN terminology—can be developed at different rates, and developers will be able to choose between providers for different services, rather than being tied to a Microsoft implementation.
OWIN開發(fā)人員為他們的應(yīng)用程序選擇所需的服務(wù),而不是像現(xiàn)在這樣單純地使用ASP.NET平臺(tái)。個(gè)別服務(wù)——在OWIN術(shù)語中稱為“Middleware(中間件)”——可能會(huì)有不同的開發(fā)速率,而開發(fā)人員將能夠在不同的服務(wù)提供商之間進(jìn)行選擇,而不是被綁定在微軟實(shí)現(xiàn)上。

There is a lot to like about the direction that OWIN and Katana are heading in, but it is in the early days, and it will be some time before it becomes a complete platform for ASP.NET applications. As I write this, it is possible to build Web API and SignalR applications without needing the System.Web namespace or IIS to process requests, but that's about all. The MVC framework requires the standard ASP.NET platform and will continue to do so for some time to come.
OWIN和Katana有很多喜歡發(fā)展的方向,但它仍處于早期時(shí)期,還需要一段時(shí)間才能成為ASP.NET應(yīng)用程序的完整平臺(tái)。在我編寫本書的時(shí)候,它已可以在不需要System.Web命名空間或者IIS處理請(qǐng)求的情況下,建立Web API和SignalR應(yīng)用程序了。MVC框架需要標(biāo)準(zhǔn)的ASP.NET平臺(tái),因此在一段時(shí)間內(nèi)仍將沿用現(xiàn)在的做法。

The ASP.NET platform and IIS are not going away. Microsoft has been clear that it sees one of the most attractive aspects of OWIN as allowing developers more flexibility in which middleware components are hosted by IIS, and Project Katana already has support for the System.Web namespaces. OWIN and Katana are not the end of ASP.NET—rather, they represent an evolution where Microsoft allows developers more flexibility in how ASP.NET applications are assembled and executed.
ASP.NET平臺(tái)以及IIS不會(huì)消失。微軟已經(jīng)清楚地看到,OWIN最有吸引力的一個(gè)方面是開發(fā)者具有了更大的靈活性,中間組件可以由IIS來托管,而Katana項(xiàng)目已經(jīng)實(shí)現(xiàn)了對(duì)System.Web命名空間的支持。OWIN和Katana不是ASP.NET的終結(jié)——而是預(yù)示著一種變革,微軟讓開發(fā)人員能夠在ASP.NET應(yīng)用程序的編譯和執(zhí)行方面更加靈活。

Tip Identity is the first major ASP.NET component to be delivered as OWIN middleware, but it won't be the last. Microsoft has made sure that the latest versions of Web API and SignalR don't depend on the System.Web namespaces, and that means that any component intended for use across the ASP.NET family of technologies has to be delivered via OWIN. I get into more detail about OWIN in my Expert ASP.NET Web API 2 for MVC Developers book, which will be published by Apress in 2014.
提示:Identity是作為OWIN中間件而交付的第一個(gè)主要的ASP.NET組件,但這不是最后一個(gè)。微軟已經(jīng)保證,Web API和SignalR的最新版本不會(huì)依賴于System.Web命名空間,這意味著,打算交叉使用ASP.NET家族技術(shù)的任何組件,都必須通過OWIN實(shí)行交付。關(guān)于OWIN,在我的Expert ASP.NET Web API 2 for MVC Developers一書中有更詳細(xì)的論述,該書已由Apress于2014年出版。

OWIN and Katana won't have a major impact on MVC framework developers for some time, but changes are already taking effect—and one of these is that ASP.NET Identity is implemented as an OWIN middleware component. This isn't ideal because it means that MVC framework applications have to mix OWIN and traditional ASP.NET platform techniques to use Identity, but it isn't too burdensome once you understand the basics and know how to get OWIN set up, which I demonstrate in the sections that follow.
OWIN和Katana在一段時(shí)間內(nèi)還不會(huì)對(duì)MVC框架開發(fā)人員發(fā)生重大沖擊,但變化正在發(fā)生——其中之一便是ASP.NET Identity要作為OWIN中間件而實(shí)現(xiàn)。這種情況不太理想,因?yàn)檫@意味著,MVC框架的應(yīng)用程序?yàn)榱耸褂肐dentity,必須將OWIN和傳統(tǒng)的ASP.NET平臺(tái)混搭在一起,這太煩瑣了,在你理解了基本概念并且知道如何建立OWIN(以下幾小節(jié)演示)之后,便會(huì)明白。

13.2.1 Creating the ASP.NET Identity Database
13.2.1 創(chuàng)建ASP.NET Identity數(shù)據(jù)庫

ASP.NET Identity isn't tied to a SQL Server schema in the same way that Membership was, but relational storage is still the default—and simplest—option, and it is the one that I will be using in this chapter. Although the NoSQL movement has gained momentum in recent years, relational databases are still the mainstream storage choice and are well-understood in most development teams.
ASP.NET Identity并未像Membership那樣,被綁定到SQL Server架構(gòu),但關(guān)系型存儲(chǔ)仍是默認(rèn)的,而且是最簡單的選擇,這也是本章中將使用的形式。雖然近年來出現(xiàn)了NoSQL運(yùn)動(dòng)勢(shì)頭,但關(guān)系型數(shù)據(jù)庫仍然是主要的存儲(chǔ)選擇,而且大多數(shù)開發(fā)團(tuán)隊(duì)都有良好理解。

ASP.NET Identity uses the Entity Framework Code First feature to automatically create its schema, but I still need to create the database into which that schema—and the user data—will be placed, just as I did in Chapter 10 when I created the database for session state data (the universal provider that I used to manage the database uses the same Code First feature).
ASP.NET Identity使用Entity Framework的Code First特性自動(dòng)地創(chuàng)建它的數(shù)據(jù)架構(gòu)(Schema),但我仍然需要?jiǎng)?chuàng)建一個(gè)用來放置此數(shù)據(jù)架構(gòu)以及用戶數(shù)據(jù)的數(shù)據(jù)庫,就像第10章所做的那樣,當(dāng)時(shí)為會(huì)話狀態(tài)數(shù)據(jù)創(chuàng)建了數(shù)據(jù)庫(用來管理數(shù)據(jù)庫的通用提供器同樣使用了Code First特性)。

Tip You don't need to understand how Entity Framework or the Code First feature works to use ASP.NET Identity.
提示:為了使用ASP.NET Identity,不一定要理解Entity Framework或Code First特性的工作機(jī)制。

As in Chapter 10, I will be using the localdb feature to create my database. As a reminder, localdb is included in Visual Studio and is a cut-down version of SQL Server that allows developers to easily create and work with databases.
正如第10章那樣,我將使用localdb特性來創(chuàng)建數(shù)據(jù)庫。要記住的是,localdb是包含在Visual Studio之中的,而且是一個(gè)簡化版的SQL Server,能夠讓開發(fā)者方便地創(chuàng)建和使用數(shù)據(jù)庫。

Select SQL Server Object Explorer from the Visual Studio View menu and right-click the SQL Server object in the window that appears. Select Add SQL Server from the pop-up menu, as shown in Figure 13-2.
從Visual Studio的“View(視圖)”菜單中選擇“SQL Server Object Explorer(SQL Server對(duì)象資源管理器)”,并在所出現(xiàn)的窗口中右擊“SQL Server”對(duì)象。從彈出菜單選擇“Add SQL Server(添加SQL Server)”,如圖13-2所示。

圖13-2

Figure 13-2. Creating a new database connection
圖13-2. 創(chuàng)建一個(gè)新的數(shù)據(jù)庫連接

Visual Studio will display the Connect to Server dialog. Set the server name to (localdb)/v11.0, select the Windows Authentication option, and click the Connect button. A connection to the database will be established and shown in the SQL Server Object Explorer window. Expand the new item, right-click Databases, and select Add New Database from the pop-up window, as shown in Figure 13-3.
Visual Studio將顯示“Connect to Server(連接到服務(wù)器)”對(duì)話框,將服務(wù)器名稱設(shè)置為(localdb)/v11.0,選擇“Windows Authentication(Windows認(rèn)證)”選項(xiàng),點(diǎn)擊“Connect(連接)”按鈕。這將建立一個(gè)數(shù)據(jù)庫連接,并顯示在“SQL Server對(duì)象資源管理器”窗口中。展開這個(gè)新項(xiàng),右擊“Databases(數(shù)據(jù)庫)”,并從彈出菜單選擇“Add New Database(添加新數(shù)據(jù)庫)”,如圖13-3所示。

圖13-3

Figure 13-3. Adding a new database
圖13-3. 添加新數(shù)據(jù)庫

Set the Database Name option to IdentityDb, leave the Database Location value unchanged, and click the OK button to create the database. The new database will be shown in the Databases section of the SQL connection in the SQL Server Object Explorer.
將“Database Name(數(shù)據(jù)庫名稱)”選項(xiàng)設(shè)置為“IdentityDb”,不用改變“Database Location(數(shù)據(jù)庫位置)”的值,點(diǎn)擊OK按鈕創(chuàng)建此數(shù)據(jù)庫。這個(gè)新數(shù)據(jù)庫將出現(xiàn)在“SQL Server對(duì)象資源管理器”中“SQL連接”的“數(shù)據(jù)庫”小節(jié)中。

13.2.2 Adding the Identity Packages
13.2.2 添加Identity包

Identity is published as a set of NuGet packages, which makes it easy to install them into any project. Enter the following commands into the Package Manager Console:
Identity是作為一組NuGet包發(fā)布的,因此易于將其安裝到任何項(xiàng)目。請(qǐng)?jiān)?#8220;Package Manager Console(包管理器控制臺(tái))”中輸入以下命令:

Install-Package Microsoft.AspNet.Identity.EntityFramework –Version 2.0.0(2.2.1)Install-Package Microsoft.AspNet.Identity.OWIN -Version 2.0.0(2.2.1)Install-Package Microsoft.Owin.Host.SystemWeb -Version 2.1.0(3.0.1)

Visual Studio can create projects that are configured with a generic user account management configuration, using the Identity API. You can add the templates and code to a project by selecting the MVC template when creating the project and setting the Authentication option to Individual User Accounts. I don't use the templates because I find them too general and too verbose and because I like to have direct control over the contents and configuration of my projects. I recommend you do the same, not least because you will gain a better understanding of how important features work, but it can be interesting to look at the templates to see how common tasks are performed.
Visual Studio能夠創(chuàng)建一些使用Identity API的項(xiàng)目,這種項(xiàng)目以“泛型用戶賬號(hào)管理配置”進(jìn)行配置。你可以給項(xiàng)目添加一些模板和代碼,只需在創(chuàng)建項(xiàng)目時(shí)選擇MVC模板,并將認(rèn)證選項(xiàng)設(shè)置為Individual User Accounts(個(gè)別用戶賬號(hào))。我沒有使用這種模板,因?yàn)槲野l(fā)現(xiàn)它們太普通,也太混亂,而且我喜歡對(duì)我項(xiàng)目中的內(nèi)容和配置有直接的控制。我建議你也這么做,這不僅讓你能夠?qū)χ匾匦缘墓ぷ鳈C(jī)制獲得更好的理解,而且,考察模板如何執(zhí)行常規(guī)任務(wù)也是有趣的。

13.2.3 Updating the Web.config File
13.2.3 更新Web.config文件

Two changes are required to the Web.config file to prepare a project for ASP.NET Identity. The first is a connection string that describes the database I created in the previous section. The second change is to define an application setting that names the class that initializes OWIN middleware and that is used to configure Identity. Listing 13-4 shows the changes I made to the Web.config file. (I explained how connection strings and application settings work in Chapter 9.)
為了做好項(xiàng)目使用ASP.NET Identity的準(zhǔn)備,需要在Web.config文件中做兩處修改。第一處是連接字符串,它描述了我在上一小節(jié)中創(chuàng)建的數(shù)據(jù)庫。第二處修改是定義一個(gè)應(yīng)用程序設(shè)置,它命名對(duì)OWIN中間件進(jìn)行初始化的類,并將它用于配置Identity。清單顯示了對(duì)Web.config文件的修改(第9章已經(jīng)解釋過連接字符串和應(yīng)用程序設(shè)置)。

Listing 13-4. Preparing the Web.config File for ASP.NET Identity
清單13-4. 為ASP.NET Identity準(zhǔn)備Web.config文件

<?xml version="1.0" encoding="utf-8"?><configuration>    <configSections>        <section name="entityFramework"            type="System.Data.Entity.Internal.ConfigFile.EntityFrameworkSection,            EntityFramework, Version=6.0.0.0, Culture=neutral,            PublicKeyToken=b77a5c561934e089" requirePermission="false" />    </configSections> 
<connectionStrings> <add name="IdentityDb" providerName="System.Data.SqlClient" connectionString="Data Source=(localdb)/v11.0; Initial Catalog=IdentityDb; Integrated Security=True; Connect Timeout=15; Encrypt=False;TrustServerCertificate=False; MultipleActiveResultSets=True"/> </connectionStrings>
<appSettings> <add key="webpages:Version" value="3.0.0.0" /> <add key="webpages:Enabled" value="false" /> <add key="ClientValidationEnabled" value="true" /> <add key="UnobtrusivejavaScriptEnabled" value="true" /> <add key="owin:AppStartup" value="Users.IdentityConfig" /> </appSettings> <system.web> <compilation debug="true" targetFramework="4.5.1" /> <httpRuntime targetFramework="4.5.1" /> </system.web> <entityFramework> <defaultConnectionFactory type="System.Data.Entity.Infrastructure.LocalDbConnectionFactory, EntityFramework"> <parameters> <parameter value="v11.0" /> </parameters> </defaultConnectionFactory> <providers> <provider invariantName="System.Data.SqlClient" type="System.Data.Entity.SqlServer.SqlProviderServices, EntityFramework.SqlServer" /> </providers> </entityFramework></configuration>

Caution Make sure you put the connectionString value on a single line. I had to break it over several lines to make the listing fit on the page, but ASP.NET expects a single, unbroken string. If in doubt, download the source code that accompanies this book, which is freely available from www.apress.com.
警告:要確保將connectionString的值放在一行中,這里把它給斷開了,是為了讓清單適應(yīng)頁面,但ASP.NET要求是單一無斷行的字符串。如果有疑問,請(qǐng)下載本書的伴隨代碼,下載地址:www.apress.com

OWIN defines its own application startup model, which is separate from the global application class that I described in Chapter 3. The application setting, called owin:AppStartup, specifies a class that OWIN will instantiate when the application starts in order to receive its configuration.
OWIN定義了它自己的應(yīng)用程序啟動(dòng)模型,與第3章所描述的全局應(yīng)用程序類是分開的。上述的應(yīng)用程序設(shè)置,名稱為owin:AppStartup,指定了一個(gè)應(yīng)用程序啟動(dòng)時(shí)OWIN會(huì)進(jìn)行實(shí)例化的類,目的是接受它的配置。

Tip Notice that I have set the MultipleActiveResultSets property to true in the connection string. This allows the results from multiple queries to be read simultaneously, which I rely on in Chapter 14 when I show you how to authorize access to action methods based on role membership.
提示:注意,我已經(jīng)在連接字符串中將MultipleActiveResultSets屬性(多活動(dòng)結(jié)果集)設(shè)置為true,這樣可以從同時(shí)讀取多個(gè)查詢形成結(jié)果,第14章依賴于這種方式,那時(shí)會(huì)演示如何根據(jù)角色成員授權(quán)訪問動(dòng)作方法。

13.2.4 Creating the Entity Framework Classes
13.2.4 創(chuàng)建Entity Framework類

If you have used Membership in projects, you may be surprised by just how much initial preparation is required for ASP.NET Identity. The extensibility that Membership lacked is readily available in ASP.NET Identity, but it comes with a price of having to create a set of implementation classes that the Entity Framework uses to manage the database. In the sections that follow, I’ll show you how to create the classes needed to get Entity Framework to act as the storage system for ASP.NET Identity.
如果你曾在項(xiàng)目使用過Membership,可能會(huì)感覺奇怪,ASP.NET Identity需要的初始化準(zhǔn)備怎么這么多。在ASP.NET Identity中具備了Membership所缺乏的可擴(kuò)展性,但其代價(jià)是需要?jiǎng)?chuàng)建一組實(shí)現(xiàn)類,由Entity Framework用于管理數(shù)據(jù)庫。在以下小節(jié)中,我將演示如何創(chuàng)建所需要的這些類,以便讓Entity Framework把它們用于ASP.NET Identity的存儲(chǔ)系統(tǒng)。

1. Creating the User Class
1. 創(chuàng)建用戶類

The first class to define is the one that represents a user, which I will refer to as the user class. The user class is derived from IdentityUser, which is defined in the Microsoft.AspNet.Identity.EntityFramework namespace. IdentityUser provides the basic user representation, which can be extended by adding properties to the derived class, which I describe in Chapter 15. Table 13-2 shows the built-in properties that IdentityUser defines, which are the ones I will be using in this chapter.
第一個(gè)要定義的類是表現(xiàn)一個(gè)用戶的類,我將它稱為“User Class(用戶類)”。這個(gè)用戶類派生于IdentityUser,它是在Microsoft.AspNet.Identity.EntityFramework命名空間中定義的。IdentityUser提供了基本的用戶表示,可以通過在它派生的類中添加屬性的辦法,對(duì)這個(gè)類進(jìn)行擴(kuò)展,我會(huì)在第15章中對(duì)此進(jìn)行描述。表13-2列出了IdentityUser所定義的內(nèi)建屬性(現(xiàn)在流行將“內(nèi)建”說成“內(nèi)置”,其實(shí)這兩者在含義上有很大差別,“內(nèi)建”完全是自己創(chuàng)建的,而“內(nèi)置”有可能是別人做的東西拿來放入其中的——譯者注),本章將使用這些屬性。

Table 13-2. The Properties Defined by the IdentityUser Class
表13-2. IdentityUser類定義的屬性
Name
名稱
Description
描述
ClaimsReturns the collection of claims for the user, which I describe in Chapter 15
返回用戶的聲明集合,關(guān)于聲明(Claims)將在第15章描述
EmailReturns the user's e-mail address
返回用戶的E-mail地址
IdReturns the unique ID for the user
返回用戶的唯一ID
LoginsReturns a collection of logins for the user, which I use in Chapter 15
返回用戶的登錄集合,將在第15章中使用
PasswordHashReturns a hashed form of the user password, which I use in the “Implementing the Edit Feature” section
返回哈希格式的用戶口令,在“實(shí)現(xiàn)Edit特性”中會(huì)用到它
RolesReturns the collection of roles that the user belongs to, which I describe in Chapter 14
返回用戶所屬的角色集合,將在第14章描述
PhoneNumberReturns the user's phone number
返回用戶的電話號(hào)碼
SecurityStampReturns a value that is changed when the user identity is altered, such as by a password change
返回變更用戶標(biāo)識(shí)時(shí)被修改的值,例如被口令修改的值
UserNameReturns the username

Tip The classes in the Microsoft.AspNet.Identity.EntityFramework namespace are the Entity Framework–specific concrete implementations of interfaces defined in the Microsoft.AspNet.Identity namespace. IdentityUser, for example, is the implementation of the IUser interface. I am working with the concrete classes because I am relying on the Entity Framework to store my user data in a database, but as ASP.NET Identity matures, you can expect to see alternative implementations of the interfaces that use different storage mechanisms (although most projects will still use the Entity Framework since it comes from Microsoft).
提示:Microsoft.AspNet.Identity.EntityFramework命名空間中的類是, Microsoft.AspNet.Identity命名空間中所定義接口的Entity Framework專用的具體實(shí)現(xiàn)。例如,IdentityUser便是IUser接口的實(shí)現(xiàn)。我會(huì)使用這些具體類,因?yàn)槲乙揽縀ntity Framework在數(shù)據(jù)庫中存儲(chǔ)我的用戶數(shù)據(jù),等到ASP.NET Identity變得成熟時(shí),你可能會(huì)期望看到這些接口的其他實(shí)現(xiàn),它們使用了不同的存儲(chǔ)機(jī)制(當(dāng)然,大多數(shù)項(xiàng)目仍然會(huì)使用Entity Framework,因?yàn)樗鼇碜杂谖④洠?/p>

What is important at the moment is that the IdentityUser class provides access only to the basic information about a user: the use's name, e-mail, phone, password hash, role memberships, and so on. If I want to store any additional information about the user, I have to add properties to the class that I derive from IdentityUser and that will be used to represent users in my application. I demonstrate how to do this in Chapter 15.
目前最重要的是這個(gè)IdentityUser類只提供了對(duì)用戶基本信息的訪問:用戶名、E-mail、電話、哈希口令、角色成員等等。如果希望存儲(chǔ)用戶的各種附加信息,就需要在IdentityUser派生的類上添加屬性,并將它用于表示應(yīng)用程序中的用戶,第15章中將演示其做法。

To create the user class for my application, I created a class file called AppUserModels.cs in the Models folder and used it to create the AppUser class, which is shown in Listing 13-5.
為了創(chuàng)建應(yīng)用程序中的用戶類,我在Models文件夾中創(chuàng)建了一個(gè)類文件,名稱為AppUserModels.cs(注意,這個(gè)文件名稱錯(cuò)了,應(yīng)當(dāng)是AppUser.cs),并用它創(chuàng)建了AppUser類,這個(gè)類如清單13-5所示。

Listing 13-5. The Contents of the AppUser.cs File
清單13-5. AppUser.cs文件的內(nèi)容

using System;using Microsoft.AspNet.Identity.EntityFramework; 
namespace Users.Models {
public class AppUser : IdentityUser { // additional properties will go here // 這里將放置附加屬性 }}

That's all I have to do at the moment, although I'll return to this class in Chapter 15, when I show you how to add application-specific user data properties.
以上便是此刻要做的全部工作,第15章會(huì)再次討論這個(gè)類,那時(shí)會(huì)演示如何添加應(yīng)用程序?qū)S玫挠脩魯?shù)據(jù)屬性。

2. Creating the Database Context Class
2. 創(chuàng)建數(shù)據(jù)庫上下文類

The next step is to create an Entity Framework database context that Operates on the AppUser class. This will allow the Code First feature to create and manage the schema for the database and provide access to the data it stores. The context class is derived from IdentityDbContext<T>, where T is the user class (AppUser in the example). I created a folder called Infrastructure in the project and added to it a class file called AppIdentityDbContext.cs, the contents of which are shown in Listing 13-6.
下一個(gè)步驟是創(chuàng)建Entity Framework數(shù)據(jù)庫的上下文,用于對(duì)AppUser類進(jìn)行操作。這可以用Code First特性來創(chuàng)建和管理數(shù)據(jù)架構(gòu),并對(duì)數(shù)據(jù)庫所存儲(chǔ)的數(shù)據(jù)進(jìn)行訪問。這個(gè)上下文類派生于IdentityDbContext<T> ,其中的T是用戶類(即此例中的AppUser)。我在項(xiàng)目中創(chuàng)建了一個(gè)文件夾,名稱為Infrastructure,并在其中添加了一個(gè)類文件,名稱為AppIdentityDbContext.cs,其內(nèi)容如清單13-6所示。

Listing 13-6. The Contents of the AppIdentityDbContext.cs File
清單13-6. AppIdentityDbContext.cs文件的內(nèi)容

using System.Data.Entity;using Microsoft.AspNet.Identity.EntityFramework;using Users.Models; 
namespace Users.Infrastructure { public class AppIdentityDbContext : IdentityDbContext<AppUser> {
public AppIdentityDbContext() : base("IdentityDb") { }
static AppIdentityDbContext() { Database.SetInitializer<AppIdentityDbContext>(new IdentityDbInit()); }
public static AppIdentityDbContext Create() { return new AppIdentityDbContext(); } }
public class IdentityDbInit : DropCreateDatabaseIfModelChanges<AppIdentityDbContext> {
protected override void Seed(AppIdentityDbContext context) { PerformInitialSetup(context); base.Seed(context); }
public void PerformInitialSetup(AppIdentityDbContext context) { // initial configuration will go here // 初始化配置將放在這兒 } }}

The constructor for the AppIdentityDbContext class calls its base with the name of the connection string that will be used to connect to the database, which is IdentityDb in this example. This is how I associate the connection string I defined in Listing 13-4 with ASP.NET Identity.
這個(gè)AppIdentityDbContext類的構(gòu)造器調(diào)用了它的基類,其參數(shù)是連接字符串的名稱,IdentityDb,用于與數(shù)據(jù)庫連接。這是讓清單13-4定義的連接字符串與ASP.NET Identity聯(lián)結(jié)起來的方法。

The AppIdentityDbContext class also defines a static constructor, which uses the Database.SetInitializer method to specify a class that will seed the database when the schema is first created through the Entity Framework Code First feature. My seed class is called IdentityDbInit, and I have provided just enough of a class to create a placeholder so that I can return to seeding the database later by adding statements to the PerformInitialSetup method. I show you how to seed the database in Chapter 14.
這個(gè)AppIdentityDbContext類還定義了一個(gè)靜態(tài)的構(gòu)造器,它使用Database.SetInitializer方法指定了一個(gè)種植數(shù)據(jù)庫的類(種植數(shù)據(jù)庫的含義是往數(shù)據(jù)庫中植入數(shù)據(jù)的意思,說穿了,就是用一些數(shù)據(jù)對(duì)數(shù)據(jù)庫進(jìn)行初始化——譯者注),當(dāng)通過Entity Framework的Code First特性第一次創(chuàng)建數(shù)據(jù)庫架構(gòu)時(shí),會(huì)用到這個(gè)類。這個(gè)種植類叫做IdentityDbInit,而且我已經(jīng)提供了一個(gè)類,創(chuàng)建了一個(gè)占位符,后面可以回過頭來在PerformInitialSetup方法中添加語句,就可以種植數(shù)據(jù)庫了。第14章將演示如何種植數(shù)據(jù)庫。

Finally, the AppIdentityDbContext class defines a Create method. This is how instances of the class will be created when needed by the OWIN, using the class I describe in the “Creating the Start Class” section.
最后,AppIdentityDbContext類定義了一個(gè)Create方法。這是由OWIN在必要時(shí)創(chuàng)建類實(shí)例的辦法,這個(gè)由OWIN使用的類將在“創(chuàng)建啟動(dòng)類”中進(jìn)行描述。

Note Don't worry if the role of these classes doesn't make sense. If you are unfamiliar with the Entity Framework, then I suggest you treat it as something of a black box. Once the basic building blocks are in place—and you can copy the ones into your chapter to get things working—then you will rarely need to edit them.
注:如果對(duì)這些類的意義無法理解,不用擔(dān)心。如果不熟悉Entity Framework,我建議你將它視為是某種黑箱事物。一旦基礎(chǔ)構(gòu)建塊就緒——而且對(duì)本章的這些代碼進(jìn)行拷貝——那么就幾乎不需要編輯它們了。

3. Creating the User Manager Class
3. 創(chuàng)建用戶管理器類

One of the most important Identity classes is the user manager, which manages instances of the user class. The user manager class must be derived from UserManager<T> , where T is the user class. The UserManager<T> class isn't specific to the Entity Framework and provides more general features for creating and operating on user data. Table 13-3 shows the basic methods and properties defined by the UserManager<T> class for managing users. There are others, but rather than list them all here, I'll describe them in context when I describe the different ways in which user data can be managed.
最重要的Identity類之一是“User Manager(用戶管理器)”,用來管理用戶類實(shí)例。用戶管理器類必須派生于UserManager<T> ,其中T是用戶類。這個(gè)UserManager<T> 類并非是專用于Entity Framework的,而且它提供了很多通用特性,用以創(chuàng)建用戶并對(duì)用戶數(shù)據(jù)進(jìn)行操作。表13-3列出了UserManager<T> 類為管理用戶而定義的基本方法和操作。還有一些其他方法,這里并未全部列出來,我會(huì)在適當(dāng)?shù)那樾蜗聦?duì)它們進(jìn)行描述,那時(shí)會(huì)介紹管理用戶數(shù)據(jù)的不同方式。

Table 13-3. The Basic Methods and Properties Defined by the UserManager<T> Class
表13-3. UserManager<T>類所定義的基本方法和操作
Name
名稱
Description
描述
ChangePasswordAsync(id, old, new)Changes the password for the specified user.
為指定用戶修改口令
CreateAsync(user)Creates a new user without a password. See Chapter 15 for an example.
創(chuàng)建一個(gè)不帶口令的新用戶,參見第15章示例
CreateAsync(user, pass)Creates a new user with the specified password. See the “Creating Users” section.
創(chuàng)建一個(gè)帶有指定口令的新用戶,參見“創(chuàng)建用戶”
DeleteAsync(user)Deletes the specified user. See the “Implementing the Delete Feature” section.
刪除指定用戶,參見“實(shí)現(xiàn)Delete特性”
FindAsync(user, pass)Finds the object that represents the user and authenticates their password. See Chapter 14 for details of authentication.
查找代表該用戶的對(duì)象,并認(rèn)證其口令,詳見第14章的認(rèn)證細(xì)節(jié)
FindByIdAsync(id)Finds the user object associated with the specified ID. See the “Implementing the Delete Feature” section.
查找與指定ID相關(guān)聯(lián)的用戶對(duì)象,參見“實(shí)現(xiàn)Delete特性”
FindByNameAsync(name) Finds the user object associated with the specified name. I use this method in the “Seeding the Database” section of Chapter 14.
查找與指定名稱相關(guān)聯(lián)的用戶對(duì)象,第14章“種植數(shù)據(jù)庫”時(shí)會(huì)用到這個(gè)方法
UpdateAsync(user)Pushes changes to a user object back into the database. See the “Implementing the Edit Feature” section.
將用戶對(duì)象的修改送入數(shù)據(jù)庫,參見“實(shí)現(xiàn)Edit特性”
UsersReturns an enumeration of the users. See the “Enumerating User Accounts” section.
返回用戶枚舉,參見“枚舉用戶賬號(hào)”

Tip Notice that the names of all of these methods end with Async. This is because ASP.NET Identity is implemented almost entirely using C# asynchronous programming features, which means that operations will be performed concurrently and not block other activities. You will see how this works once I start demonstrating how to create and manage user data. There are also synchronous extension methods for each Async method. I stick to the asynchronous methods for most examples, but the synchronous equivalents are useful if you need to perform multiple related operations in sequence. I have included an example of this in the “Seeding the Database” section of Chapter 14. The synchronous methods are also useful when you want to call Identity methods from within property getters or setters, which I do in Chapter 15.
提示:請(qǐng)注意方法名以Async結(jié)尾的那些方法。因?yàn)锳SP.NET Identity幾乎完全是用C#的異步編程特性實(shí)現(xiàn)的,這意味著會(huì)并發(fā)地執(zhí)行各種操作,而不會(huì)阻塞其他活動(dòng)。在我開始演示如何創(chuàng)建和管理用戶數(shù)據(jù)時(shí),你便會(huì)看到這種情況。對(duì)于每一個(gè)Async方法也有相應(yīng)的同步擴(kuò)展方法。對(duì)于大多數(shù)示例,我會(huì)堅(jiān)持這種異步方法。但是,如果你需要按順序執(zhí)行一些相關(guān)的操作,同步方法是有用的。在第14章的“種植數(shù)據(jù)庫”中就包含了一個(gè)這樣的例子。當(dāng)你希望從屬性內(nèi)部的getter或setter代碼塊中調(diào)用Identity方法時(shí),同步方法也是有用的,在第15章中,我就是這么做的。

I added a class file called AppUserManager.cs to the Infrastructure folder and used it to define the user manager class, which I have called AppUserManager, as shown in Listing 13-7.
我在Infrastructure文件夾中添加了一個(gè)類文件,名稱為AppUserManager.cs,用它定義了用戶管理器類,名稱為AppUserManager,如清單13-7所示。

Listing 13-7. The Contents of the AppUserManager.cs File
清單13-7. AppUserManager.cs文件的內(nèi)容

using Microsoft.AspNet.Identity;using Microsoft.AspNet.Identity.EntityFramework;using Microsoft.AspNet.Identity.Owin;using Microsoft.Owin;using Users.Models; 
namespace Users.Infrastructure { public class AppUserManager : UserManager<AppUser> {
public AppUserManager(IUserStore<AppUser> store) : base(store) { }
public static AppUserManager Create( IdentityFactoryOptions<AppUserManager> options, IOwinContext context) { AppIdentityDbContext db = context.Get<AppIdentityDbContext>(); AppUserManager manager = new AppUserManager(new UserStore<AppUser>(db)); return manager; } }}

The static Create method will be called when Identity needs an instance of the AppUserManager, which will happen when I perform operations on user data—something that I will demonstrate once I have finished performing the setup.
在Identity需要一個(gè)AppUserManager的實(shí)例時(shí),將會(huì)調(diào)用靜態(tài)的Create方法,這種情況將在對(duì)用戶數(shù)據(jù)執(zhí)行操作時(shí)發(fā)生——也是在完成設(shè)置之后要演示的事情。

To create an instance of the AppUserManager class, I need an instance of UserStore<AppUser> . The UserStore<T> class is the Entity Framework implementation of the IUserStore<T> interface, which provides the storage-specific implementation of the methods defined by the UserManager class. To create the UserStore<AppUser> , I need an instance of the AppIdentityDbContext class, which I get through OWIN as follows:
為了創(chuàng)建AppUserManager類的實(shí)例,我需要一個(gè)UserStore<AppUser> 實(shí)例。這個(gè)UserStore<T> 類是IUserStore<T> 接口的Entity Framework實(shí)現(xiàn),它提供了UserManager類所定義的存儲(chǔ)方法的實(shí)現(xiàn)。為了創(chuàng)建UserStore<AppUser> ,又需要AppIdentityDbContext類的一個(gè)實(shí)例,這是通過OWIN按如下辦法得到的:

...AppIdentityDbContext db = context.Get<AppIdentityDbContext>();...

The IOwinContext implementation passed as an argument to the Create method defines a generically typed Get method that returns instances of objects that have been registered in the OWIN start class, which I describe in the following section.
被作為參數(shù)傳遞給Create方法的IOwinContext實(shí)現(xiàn)定義了一個(gè)泛型的Get方法,它會(huì)返回已經(jīng)在OWIN啟動(dòng)類中注冊(cè)的對(duì)象實(shí)例,啟動(dòng)類在以下小節(jié)描述。

4. Creating the Start Class
4. 創(chuàng)建啟動(dòng)類

The final piece I need to get ASP.NET Identity up and running is a start class. In Listing 13-4, I defined an application setting that specified a configuration class for OWIN, like this:
為了使ASP.NET Identity就緒并能運(yùn)行,要做的最后一個(gè)片段是Start Class(啟動(dòng)類)。在清單13-4中,我定義了一個(gè)應(yīng)用程序設(shè)置,它為OWIN指定配置類,如下所示:

...<add key="owin:AppStartup" value="Users.IdentityConfig" />...

OWIN emerged independently of ASP.NET and has its own conventions. One of them is that there is a class that is instantiated to load and configure middleware and perform any other configuration work that is required. By default, this class is called Start, and it is defined in the global namespace. This class contains a method called Configuration, which is called by the OWIN infrastructure and passed an implementation of the Owin.IAppBuilder interface, which supports setting up the middleware that an application requires. The Start class is usually defined as a partial class, with its other class files dedicated to each kind of middleware that is being used.
OWIN是獨(dú)立出現(xiàn)在ASP.NET中的,并且有它自己的約定。其中之一就是,為了加載和配置中間件,并執(zhí)行所需的其他配置工作,需要有一個(gè)被實(shí)例化的類。默認(rèn)情況下,這個(gè)類叫做Start,而且是在全局命名空間中定義的。這個(gè)類含有一個(gè)名稱為Configuration的方法,該方法由OWIN基礎(chǔ)架構(gòu)進(jìn)行調(diào)用,并為該方法傳遞一個(gè)Owin.IAppBuilder接口的實(shí)現(xiàn),由它支持應(yīng)用程序所需中間件的設(shè)置。Start類通常被定義成分部類,還有一些其他的類文件,它們分別專用于要用的每一種中間件。

I freely ignore this convention, given that the only OWIN middleware that I use in MVC framework applications is Identity. I prefer to use the application setting in the Web.config file to define a single class in the top-level namespace of the application. To this end, I added a class file called IdentityConfig.cs to the App_Start folder and used it to define the class shown in Listing 13-8, which is the class that I specified in the Web.config folder.
我隨意地忽略了這一約定,因?yàn)槲以贛VC框架應(yīng)用程序中使用的唯一OWIN中間件就是Identity。為了在應(yīng)用程序的頂級(jí)命名空間定義一個(gè)類,我喜歡在Web.config文件中使用應(yīng)用程序設(shè)置。于是我在App_Start文件夾中添加了一個(gè)類文件,名稱為IdentityConfig.cs,并用它定義了如清單13-8所示的類,這是我在Web.config文件中指定的一個(gè)類。

Listing 13-8. The Contents of the IdentityConfig.cs File
清單13-8. IdentityConfig.cs文件的內(nèi)容

using Microsoft.AspNet.Identity;using Microsoft.Owin;using Microsoft.Owin.Security.Cookies;using Owin;using Users.Infrastructure; 
namespace Users { public class IdentityConfig { public void Configuration(IAppBuilder app) {
app.CreatePerOwinContext<AppIdentityDbContext>(AppIdentityDbContext.Create); app.CreatePerOwinContext<AppUserManager>(AppUserManager.Create);
app.UseCookieAuthentication(new CookieAuthenticationOptions { AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie, LoginPath = new PathString("/Account/Login"), }); } }}

The IAppBuilder interface is supplemented by a number of extension methods defined in classes in the Owin namespace. The CreatePerOwinContext method creates a new instance of the AppUserManager and AppIdentityDbContext classes for each request. This ensures that each request has clean access to the ASP.NET Identity data and that I don't have to worry about synchronization or poorly cached database data.
IAppBuilder接口是由一些擴(kuò)展方法提供的,這些擴(kuò)展方法的定義在Owin命名空間的一些類中。CreatePerOwinContext用于創(chuàng)建AppUserManager的新實(shí)例,AppIdentityDbContext類用于每一個(gè)請(qǐng)求。這樣保證每一個(gè)請(qǐng)求對(duì)ASP.NET Identity數(shù)據(jù)有清晰的訪問,我不必為同步時(shí)的情況或匱乏的數(shù)據(jù)緩存而操心。

The UseCookieAuthentication method tells ASP.NET Identity how to use a cookie to identity authenticated users, where the options are specified through the CookieAuthenticationOptions class. The important part here is the LoginPath property, which specifies a URL that clients should be redirected to when they request content without authentication. I have specified /Account/Login, and I will create the controller that handles these redirections in Chapter 14.
UseCookieAuthentication方法告訴ASP.NET Identity如何用Cookie去標(biāo)識(shí)已認(rèn)證的用戶,以及通過CookieAuthenticationOptions類指定的選項(xiàng)在哪兒。這里重要的部分是LoginPath屬性,它指定了一個(gè)URL,這是未認(rèn)證客戶端請(qǐng)求內(nèi)容時(shí)要重定向的地址。我在其中指定了/Account/Login,將在第14章創(chuàng)建這個(gè)控制器來處理這些重定向。

13.3 Using ASP.NET Identity
13.3 使用ASP.NET Identity

Now that the basic setup is out of the way, I can start to use ASP.NET Identity to add support for managing users to the example application. In the sections that follow, I will demonstrate how the Identity API can be used to create administration tools that allow for centralized management of users. Table 13-4 puts ASP.NET Identity into context.
現(xiàn)在,已經(jīng)完成了基本設(shè)置,可以開始使用ASP.NET Identity在示例應(yīng)用程序中添加對(duì)用戶管理的支持了。在以下幾小節(jié)中,我將演示如何將Identity API用于創(chuàng)建管理工具,這樣能夠集中化地管理用戶。表13-4說明了ASP.NET Identity的情形。

Table 13-4. Putting Content Caching by Attribute in Context
表13-4. ASP.NET Identity的情形(這個(gè)標(biāo)題作者寫錯(cuò)了——譯者注)
Question
問題
Answer
回答
What is it?
什么是ASP.NET Identity?
ASP.NET Identity is the API used to manage user data and perform authentication and authorization.
ASP.NET Identity是用來管理用戶數(shù)據(jù)并執(zhí)行認(rèn)證和授權(quán)的API
Why should I care?
為什么要關(guān)注它?
Most applications require users to create accounts and provide credentials to access content and features. ASP.NET Identity provides the facilities for performing these operations.
大多數(shù)應(yīng)用程序都需要用戶創(chuàng)建賬號(hào),并提供憑據(jù)去訪問內(nèi)容和功能。ASP.NET Identity提供了執(zhí)行這些操作的工具
How is it used by the MVC framework?
如何在MVC框架中使用它?
ASP.NET Identity isn't used directly by the MVC framework but integrates through the standard MVC authorization features.
ASP.NET Identity不是由MVC框架直接使用的,但它集成了標(biāo)準(zhǔn)的MVC授權(quán)特性

13.3.1 Enumerating User Accounts
13.3.1 枚舉用戶賬號(hào)

Centralized user administration tools are useful in just about all applications, even those that allow users to create and manage their own accounts. There will always be some customers who require bulk account creation, for example, and support issues that require inspection and adjustment of user data. From the perspective of this chapter, administration tools are useful because they consolidate a lot of basic user management functions into a small number of classes, making them useful examples to demonstrate the fundamental features of ASP.NET Identity.
集中化的用戶管理工具在幾乎所有應(yīng)用程序中都是有用的,即使是那些允許用戶創(chuàng)建并管理自己賬號(hào)的情況也是如此。例如,總會(huì)有這樣一些客戶,他們需要大量創(chuàng)建賬號(hào),并支持需要檢查和調(diào)整用戶數(shù)據(jù)的問題。根據(jù)本章的觀點(diǎn),管理工具是有用的,因?yàn)樗鼈儗⒃S多基本的用戶管理功能整合到了少量的幾個(gè)類之中,這對(duì)于演示ASP.NET Identity的基本特性是很有用的。

I started by adding a controller called Admin to the project, which is shown in Listing 13-9, and which I will use to define my user administration functionality.
首先,在項(xiàng)目中添加一個(gè)控制器,名稱為Admin,如清單13-9所示,我將用它定義我的用戶管理功能。

Listing 13-9. The Contents of the AdminController.cs File
清單13-9. AdminController.cs文件的內(nèi)容

using System.Web;using System.Web.Mvc;using Microsoft.AspNet.Identity.Owin;using Users.Infrastructure; 
namespace Users.Controllers { public class AdminController : Controller {
public ActionResult Index() { return View(UserManager.Users); }
private AppUserManager UserManager { get { return HttpContext.GetOwinContext().GetUserManager<AppUserManager>(); } } }}

The Index action method enumerates the users managed by the Identity system; of course, there aren't any users at the moment, but there will be soon. The important part of this listing is the way that I obtain an instance of the AppUserManager class, through which I manage user information. I will be using the AppUserManager class repeatedly as I implement the different administration functions, and I defined the UserManager property in the Admin controller to simplify my code.
動(dòng)作方法枚舉由Identity系統(tǒng)管理的用戶,當(dāng)然,此刻還沒有任何用戶,但馬上就會(huì)有了。該清單重要的部分是我獲得類實(shí)例的方式,通過它,我可以管理用戶信息。在我實(shí)現(xiàn)不同的管理功能時(shí),我會(huì)反復(fù)使用AppUserManager類,而且,我在Admin控制器中定義了UserManager屬性,以便簡化代碼。

The Microsoft.Owin.Host.SystemWeb assembly adds extension methods for the HttpContext class, one of which is GetOwinContext. This provides a per-request context object into the OWIN API through an IOwinContext object. The IOwinContext isn't that interesting in an MVC framework application, but there is another extension method called GetUserManager<T> that is used to get instances of the user manager class.
Microsoft.Owin.Host.SystemWeb程序集為HttpContext類添加了一些擴(kuò)展方法,其中之一便是GetOwinContext。它通過一個(gè)IOwinContext對(duì)象,將基于每請(qǐng)求的上下文對(duì)象提供給WOIN API。MVC框架應(yīng)用程序?qū)?span id="70qarhlowxag" class="code">IOwinContext不感興趣,但是有另外一個(gè)擴(kuò)展方法,叫做GetUserManager<T> ,可以用來得到用戶管理器類的實(shí)例。

Tip As you may have gathered, there are lots of extension methods in ASP.NET Identity; overall, the API is something of a muddle as it tries to mix OWIN, abstract Identity functionality, and the concrete Entity Framework storage implementation.
提示:正如你所推斷的一樣,ASP.NET Identity中有許多擴(kuò)展方法,總體而言,這個(gè)API是一種混合體,它試圖將WOIN、抽象Identity功能以及具體的Entity Framework存儲(chǔ)實(shí)現(xiàn)混合在一起。

I called the GetUserManager with a generic type parameter to specify the AppUserManager class that I created earlier in the chapter, like this:
我用一個(gè)泛型參數(shù)調(diào)用了GetUserManager,用以指定本章前面創(chuàng)建的AppUserManager類,如下所示:

...return HttpContext.GetOwinContext().GetUserManager<AppUserManager>();...

Once I have an instance of the AppUserManager class, I can start to query the data store. The AppUserManager.Users property returns an enumeration of user objects—instances of the AppUser class in my application—which can be queried and manipulated using LINQ.
一旦有了AppUserManager類的實(shí)例,便可以開始查詢數(shù)據(jù)存儲(chǔ)了。AppUserManager.Users屬性返回了一個(gè)用戶對(duì)象的枚舉——應(yīng)用程序中AppUser類的實(shí)例——于是可以用LINQ對(duì)這個(gè)枚舉進(jìn)行查詢和維護(hù)。

In the Index action method, I pass the value of the Users property to the View method so that I can list details of the users in the view. Listing 13-10 shows the contents of the Views/Admin/Index.cshtml file that I created by right-clicking the Index action method and selecting Add View from the pop-up menu.
Index動(dòng)作方法中,給View方法傳遞了Users屬性的值,以便能夠在視圖列出用戶的細(xì)節(jié)。清單13-10顯示了Views/Admin/Index.cshtml文件的內(nèi)容,這是通過右擊Index動(dòng)作方法,并從彈出菜單選擇“Add View(添加視圖)”而創(chuàng)建的。

Listing 13-10. The Contents of the Index.cshtml File in the /Views/Admin Folder
清單13-10. /Views/Admin文件夾中Index.cshtml文件的內(nèi)容

@using Users.Models@model IEnumerable<AppUser>@{    ViewBag.Title = "Index";}
<div class="panel panel-primary"> <div class="panel-heading"> User Accounts </div> <table class="table table-striped">
<tr><th>ID</th><th>Name</th><th>Email</th></tr> @if (Model.Count() == 0) { <tr><td colspan="3" class="text-center">No User Accounts</td></tr> } else { foreach (AppUser user in Model) { <tr> <td>@user.Id</td> <td>@user.UserName</td> <td>@user.Email</td> </tr> } } </table></div>@Html.ActionLink("Create", "Create", null, new { @class = "btn btn-primary" })

This view contains a table that has rows for each user, with columns for the unique ID, username, and e-mail address. If there are no users in the database, then a message is displayed, as shown in Figure 13-4.
這個(gè)視圖含有一個(gè)表格,每個(gè)用戶為一行,帶有唯一ID、用戶名以及E-mail地址的表格列。如果數(shù)據(jù)庫中沒有用戶,那么會(huì)顯示一條消息(圖中的“No User Accounts”消息——譯者注),如圖13-4所示。

圖13-4

Figure 13-4. Display the (empty) list of users
圖13-4. 顯示用戶列表(空)

I included a Create link in the view (which I styled as a button using Bootstrap) that targets the Create action on the Admin controller. I'll implement this action shortly to support adding users.
在視圖中有一個(gè)Create的鏈接(其樣式用Bootstrap形成了一個(gè)按鈕),它的目標(biāo)是Admin控制器中的Create動(dòng)作。為了支持添加用戶,我會(huì)很快實(shí)現(xiàn)這個(gè)動(dòng)作。

RESETTING THE DATABASE
重置數(shù)據(jù)庫

When you start the application and navigate to the /Admin/Index URL, it will take a few moments before the contents rendered from the view are displayed. This is because the Entity Framework has connected to the database and determined that there is no schema defined. The Code First feature uses the classes I defined earlier in the chapter (and some which are contained in the Identity assemblies) to create the schema so that it is ready to be queried and to store data.
當(dāng)啟動(dòng)應(yīng)用程序并導(dǎo)航到/Admin/Index URL時(shí),在視圖渲染的內(nèi)容被顯示出來之前,會(huì)花一些時(shí)間。這是因?yàn)镋ntity Framework已經(jīng)鏈接到數(shù)據(jù)庫,并發(fā)現(xiàn)此時(shí)尚未定義數(shù)據(jù)庫架構(gòu)。Code First特性使用本章前面定義的類(以及包含在Identity程序集中的類)創(chuàng)建該架構(gòu),以便做好查詢和存儲(chǔ)數(shù)據(jù)的準(zhǔn)備。

You can see the effect by opening the Visual Studio SQL Server Object Explorer window and expanding entry for the IdentityDB database schema, which will include tables with names such as AspNetUsers and AspNetRoles.
通過打開Visual Studio的“SQL Server Object Explorer(SQL Server對(duì)象資源管理器)”窗口,并展開IdentityDB數(shù)據(jù)庫架構(gòu),便可以看到其中包含了一些數(shù)據(jù)表,如AspNetUsersAspNetRoles等等 。

To delete the database, right-click the IdentityDb item and select Delete from the pop-up menu. Check both of the options in the Delete Database dialog and click the OK button to delete the database.
要?jiǎng)h除該數(shù)據(jù)庫,右擊IdentityDb條目,并從彈出菜單選擇“Delete(刪除)”。選中“Delete Database(刪除數(shù)據(jù)庫)”對(duì)話框中的復(fù)選框,并點(diǎn)擊OK,以刪除該數(shù)據(jù)庫。

Right-click the Databases item, select Add New Database (as shown in Figure 13-3), and enter IdentityDb in the Database Name field. Click OK to create the empty database. The next time you start the application and navigate to the Admin/Index URL, the Entity Framework will detect that there is no schema and re-create it.
右擊“Databases(數(shù)據(jù)庫)”條目,選擇“Add New Database(添加新數(shù)據(jù)庫)”(如圖13-3所示),并在“Database Name(數(shù)據(jù)庫名)”字段中輸入IdentityDb,點(diǎn)擊OK,便可以創(chuàng)建空數(shù)據(jù)庫。下一次啟動(dòng)應(yīng)用程序,并導(dǎo)航到Admin/Index URL時(shí),Entity Framework又將偵測(cè)到?jīng)]有數(shù)據(jù)庫架構(gòu),又會(huì)重新創(chuàng)建此數(shù)據(jù)庫。

13.3.2 Creating Users
13.3.2 創(chuàng)建用戶

I am going to use MVC framework model validation for the input my application receives, and the easiest way to do this is to create simple view models for each of the operations that my controller supports. I added a class file called UserViewModels.cs to the Models folder and used it to define the class shown in Listing 13-11. I'll add further classes to this file as I define models for additional features.
對(duì)于應(yīng)用程序接收的輸入,我打算使用MVC架構(gòu)的模型驗(yàn)證,最容易的做法是為控制器所支持的每一種操作創(chuàng)建一個(gè)簡單的視圖模型。我在Models文件夾中添加了一個(gè)類文件,名稱為serViewModels.cs,并用它定義了如清單13-11所示的類。隨著我為其他特性定義模型,會(huì)進(jìn)一步地在這個(gè)文件中添加一些類。

Listing 13-11. The Contents of the UserViewModels.cs File
清單13-11. UserViewModels.cs文件的內(nèi)容

using System.ComponentModel.DataAnnotations; 
namespace Users.Models {
public class CreateModel { [Required] public string Name { get; set; } [Required] public string Email { get; set; } [Required] public string Password { get; set; } }}

The initial model I have defined is called CreateModel, and it defines the basic properties that I require to create a user account: a username, an e-mail address, and a password. I have used the Required attribute from the System.ComponentModel.DataAnnotations namespace to denote that values are required for all three properties defined in the model.
我所定義的第一個(gè)模型叫做CreateModel,它定義了創(chuàng)建用戶賬號(hào)所需要的基本屬性:用戶名、E-mail地址以及口令。這個(gè)模型中所定義的所有屬性都使用了System.ComponentModel.DataAnnotations命名空間中的Required注釋屬性,用以說明這些值是必須的。

I added a pair of Create action methods to the Admin controller, which are targeted by the link in the Index view from the previous section and which uses the standard controller pattern to present a view to the user for a GET request and process form data for a POST request. You can see the new action methods in Listing 13-12.
我在Admin控制器中添加了一對(duì)Create動(dòng)作方法,它們是上一小節(jié)Index視圖中的那個(gè)鏈接所指向的目標(biāo),而且,對(duì)于GET請(qǐng)求以及處理表單數(shù)據(jù)的POST請(qǐng)求,使用的是標(biāo)準(zhǔn)的控制器模式將視圖表現(xiàn)給用戶。可以從清單13-12中看到這兩個(gè)新的動(dòng)作方法。

Listing 13-12. Defining the Create Action Methods in the AdminController.cs File
清單13-12. 在AdminController.cs文件中定義Create動(dòng)作方法

using System.Web;using System.Web.Mvc;using Microsoft.AspNet.Identity.Owin;using Users.Infrastructure;using Users.Models;using Microsoft.AspNet.Identity;using System.Threading.Tasks; 
namespace Users.Controllers { public class AdminController : Controller {
public ActionResult Index() { return View(UserManager.Users); }
public ActionResult Create() { return View(); }
[HttpPost] public async Task<ActionResult> Create(CreateModel model) { if (ModelState.IsValid) { AppUser user = new AppUser {UserName = model.Name, Email = model.Email}; IdentityResult result = await UserManager.CreateAsync(user, model.Password); if (result.Succeeded) { return RedirectToAction("Index"); } else { AddErrorsFromResult(result); } } return View(model); }
private void AddErrorsFromResult(IdentityResult result) { foreach (string error in result.Errors) { ModelState.AddModelError("", error); } }

private AppUserManager UserManager { get { return HttpContext.GetOwinContext().GetUserManager<AppUserManager>(); } } }}

The important part of this listing is the Create method that takes a CreateModel argument and that will be invoked when the administrator submits their form data. I use the ModelState.IsValid property to check that the data I am receiving contains the values I require, and if it does, I create a new instance of the AppUser class and pass it to the UserManager.CreateAsync method, like this:
該清單重要的部分是以CreateModel為參數(shù)并在管理員遞交其表單數(shù)據(jù)時(shí)調(diào)用的那個(gè)Create動(dòng)作方法(POST版的Create動(dòng)作方法——譯者注)。我用ModelState.IsValid屬性檢查所接收到的數(shù)據(jù)是否包含了我所需要的數(shù)據(jù),如果是,便創(chuàng)建一個(gè)AppUser類的新實(shí)例,并將它傳遞給UserManager.CreateAsync方法,如下所示:

...AppUser user = new AppUser {UserName = model.Name, Email = model.Email};IdentityResult result = await UserManager.CreateAsync(user, model.Password); ...

The result from the CreateAsync method is an implementation of the IdentityResult interface, which describes the outcome of the operation through the properties listed in Table 13-5.
CreateAsync方法的結(jié)果是一個(gè)IdentityResult接口的實(shí)現(xiàn),它通過表13-5中的屬性描述操作的輸出。

Table 13-5. The Properties Defined by the IdentityResult Interface
表13-5. IdentityResult接口所定義的屬性
Name
名稱
Description
描述
ErrorsReturns a string enumeration that lists the errors encountered while attempting the operation
返回一個(gè)字符串枚舉,其中列出了嘗試操作期間所遇到的錯(cuò)誤
SucceededReturns true if the operation succeeded
在操作成功時(shí)返回true

USING THE ASP.NET IDENTITY ASYNCHRONOUS METHODS
使用ASP.NET Identity異步方法

You will notice that all of the operations for manipulating user data, such as the UserManager.CreateAsync method I used in Listing 13-12, are available as asynchronous methods. Such methods are easily consumed with the async and await keywords. Using asynchronous Identity methods allows your action methods to be executed asynchronously, which can improve the overall throughput of your application.
你會(huì)注意到,維護(hù)用戶數(shù)據(jù)的所有操作,如清單13-12中所用到的UserManager.CreateAsync方法,都可以作為異步方法來使用。這種方法很容易與asyncawait關(guān)鍵字一起使用。使用異步的Identity方法讓你的動(dòng)作方法能夠異步執(zhí)行,這可以從整體上改善應(yīng)用程序。

However, you can also use synchronous extension methods provided by the Identity API. All of the commonly used asynchronous methods have a synchronous wrapper so that the functionality of the UserManager.CreateAsync method can be called through the synchronous UserManager.Create method. I use the asynchronous methods for preference, and I recommend you follow the same approach in your projects. The synchronous methods can be useful for creating simpler code when you need to perform multiple dependent operations, so I used them in the “Seeding the Database” section of Chapter 14 as a demonstration.
然而,你也可以使用那些由Identity API提供的同步擴(kuò)展方法。所有常用的異步方法都有一個(gè)同步的封裝程序,因此UserManager.CreateAsync方法的功能可以通過同步的UserManager.Create方法進(jìn)行調(diào)用。我更喜歡使用異步方法,而且我建議在你的項(xiàng)目中遵循同樣的方式。當(dāng)需要執(zhí)行多個(gè)有依賴的操作時(shí),為了形成簡單的代碼,同步方法可能是有用的,因此,作為一個(gè)演示,我在第14章的“種植數(shù)據(jù)庫”小節(jié)中使用了它們。

I inspect the Succeeded property in the Create action method to determine whether I have been able to create a new user record in the database. If the Succeeded property is true, then I redirect the browser to the Index action so that list of users is displayed:
Create動(dòng)作方法中,我檢測(cè)了Succeeded屬性,以確定是否能夠在數(shù)據(jù)庫創(chuàng)建一條新的用戶記錄。如果Succeeded屬性為true,那么便將瀏覽器重定向到Index動(dòng)作,以便顯示用戶列表:

...if (result.Succeeded) {    return RedirectToAction("Index");} else {    AddErrorsFromResult(result);}...

If the Succeeded property is false, then I call the AddErrorsFromResult method, which enumerates the messages from the Errors property and adds them to the set of model state errors, taking advantage of the MVC framework model validation feature. I defined the AddErrorsFromResult method because I will have to process errors from other operations as I build the functionality of my administration controller. The last step is to create the view that will allow the administrator to create new accounts. Listing 13-13 shows the contents of the Views/Admin/Create.cshtml file.
如果Succeeded屬性為false,那么便調(diào)用AddErrorsFromResult方法,該方法枚舉了Errors屬性中的消息,并將它們添加到模型狀態(tài)的錯(cuò)誤消息集合中,此過程利用了MVC框架的模型驗(yàn)證特性。我定義AddErrorsFromResult方法,是因?yàn)殡S著進(jìn)一步地構(gòu)建這個(gè)管理控制器的功能,還必須處理來自其他操作的錯(cuò)誤消息。最后一步是創(chuàng)建視圖,讓管理員創(chuàng)建新賬號(hào)。清單13-13顯示了文件的Views/Admin/Create.cshtml內(nèi)容。

Listing 13-13. The Contents of the Create.cshtml File
清單13-13. Create.cshtml文件的內(nèi)容

@model Users.Models.CreateModel@{ ViewBag.Title = "Create User";}
<h2>Create User</h2>@Html.ValidationSummary(false)@using (Html.BeginForm()) { <div class="form-group"> <label>Name</label> @Html.TextBoxFor(x => x.Name, new { @class = "form-control"}) </div> <div class="form-group"> <label>Email</label> @Html.TextBoxFor(x => x.Email, new { @class = "form-control" }) </div> <div class="form-group"> <label>Password</label> @Html.PasswordFor(x => x.Password, new { @class = "form-control" }) </div> <button type="submit" class="btn btn-primary">Create</button> @Html.ActionLink("Cancel", "Index", null, new { @class = "btn btn-default"})}

There is nothing special about this view—it is a simple form that gathers values that the MVC framework will bind to the properties of the model class that is passed to the Create action method.
該視圖沒有什么特別的地方——只是一個(gè)簡單的表單,用來收集一些值,MVC框架會(huì)將它們綁定到模型類的屬性上,然后傳遞給Create動(dòng)作方法。

1. Testing the Create Functionality
1. 測(cè)試Create功能

To test the ability to create a new user account, start the application, navigate to the /Admin/Index URL, and click the Create button. Fill in the form with the values shown in Table 13-6.
為了測(cè)試創(chuàng)建新用戶賬號(hào)的能力,啟動(dòng)應(yīng)用程序,導(dǎo)航到/Admin/Index網(wǎng)址,并點(diǎn)擊Create按鈕。用一些值填充表單,如表13-6所示。

Table 13-6. The Values for Creating an Example User
表13-6. 創(chuàng)建示例用戶的值
Name
名稱
Value
Name
用戶名
Joe
Email
E-mail地址
[email protected]
Password
口令
Secret

Tip Although not widely known by developers, there are domains that are reserved for testing, including example.com. You can see a complete list at https://tools.ietf.org/html/rfc2606.
提示:雖然不是很多開發(fā)人員都知道,有一些域名是保留用于測(cè)試的,包括example.com。可以在https://tools.ietf.org/html/rfc2606網(wǎng)站看到完整的列表。

Once you have entered the values, click the Create button. ASP.NET Identity will create the user account, which will be displayed when your browser is redirected to the Index action method, as shown in Figure 13-5. You will see a different ID value because IDs are randomly generated for each user account.
一旦輸入了這些值,點(diǎn)擊“Create”按鈕。ASP.NET Identity將創(chuàng)建此用戶賬號(hào),當(dāng)瀏覽器被重定向到Index動(dòng)作方法,會(huì)顯示出該用戶的信息,如圖13-5所示。從圖中可以看到不同的ID值,這是因?yàn)閷?duì)于每個(gè)用戶賬號(hào),ID是隨機(jī)生成的。

圖13-5

Figure 13-5. The effect of adding a new user account
圖13-5. 添加新用戶賬號(hào)的效果

Click the Create button again and enter the same details into the form, using the values in Table 13-6. This time when you submit the form, you will see an error reported through the model validation summary, as shown in Figure 13-6.
再次點(diǎn)擊“Create”按鈕,并在表章中輸入同樣細(xì)節(jié),即,使用表13-6中的值。當(dāng)這一次遞交表單時(shí),會(huì)看到一條通過模型驗(yàn)證摘要報(bào)告的錯(cuò)誤,如圖13-6所示。

圖13-6

Figure 13-6. An error trying to create a new user
圖13-6. 試圖創(chuàng)建新用戶時(shí)的錯(cuò)誤

13.3.3 Validating Passwords
13.3.3 驗(yàn)證口令

One of the most common requirements, especially for corporate applications, is to enforce a password policy. ASP.NET Identity provides the PasswordValidator class, which can be used to configure a password policy using the properties described in Table 13-7.
一個(gè)最常用的需求,特別是對(duì)于公司的應(yīng)用程序,是強(qiáng)制口令策略。ASP.NET Identity提供了一個(gè)PasswordValidator類,可以用表13-7所描述的屬性來配置口令策略。

Table 13-7. The Properties Defined by the PasswordValidator Class
表13-7. PasswordValidator類定義的屬性
Name
名稱
Description
描述
RequiredLengthSpecifies the minimum length of a valid passwords.
指定合法口令的最小長度
RequireNonLetterOrDigitWhen set to true, valid passwords must contain a character that is neither a letter nor a digit.
當(dāng)設(shè)置為true時(shí),合法口令必須含有非字母和數(shù)字的字符
RequireDigitWhen set to true, valid passwords must contain a digit.
當(dāng)設(shè)置為true時(shí),合法口令必須含有數(shù)字
RequireLowercaseWhen set to true, valid passwords must contain a lowercase character.
當(dāng)設(shè)置為true時(shí),合法口令必須含有小寫字母
RequireUppercaseWhen set to true, valid passwords must contain an uppercase character.
當(dāng)設(shè)置為true時(shí),合法口令必須含有大寫字母

A password policy is defined by creating an instance of the PasswordValidator class, setting the property values, and using the object as the value for the PasswordValidator property in the Create method that OWIN uses to instantiate the AppUserManager class, as shown in Listing 13-14.
定義口令策略的辦法是,創(chuàng)建一個(gè)PasswordValidator類實(shí)例、設(shè)置其屬性的值,并在OWIN用來實(shí)例化AppUserManager類的Create方法中將該對(duì)象作為PasswordValidator屬性的值,如清單13-14所示。

Listing 13-14. Setting a Password Policy in the AppUserManager.cs File
清單13-14. 在AppUserManager.cs文件中設(shè)置口令策略

using Microsoft.AspNet.Identity;using Microsoft.AspNet.Identity.EntityFramework;using Microsoft.AspNet.Identity.Owin;using Microsoft.Owin;using Users.Models; 
namespace Users.Infrastructure { public class AppUserManager : UserManager<AppUser> {
public AppUserManager(IUserStore<AppUser> store) : base(store) { } public static AppUserManager Create(IdentityFactoryOptions<AppUserManager> options, IOwinContext context) {
AppIdentityDbContext db = context.Get<AppIdentityDbContext>(); AppUserManager manager = new AppUserManager( new UserStore<AppUser>(db));
manager.PasswordValidator = new PasswordValidator { RequiredLength = 6, RequireNonLetterOrDigit = false, RequireDigit = false, RequireLowercase = true, RequireUppercase = true };

return manager; } }}

I used the PasswordValidator class to specify a policy that requires at least six characters and a mix of uppercase and lowercase characters. You can see how the policy is applied by starting the application, navigating to the /Admin/Index URL, clicking the Create button, and trying to create an account that has the password secret. The password doesn't meet the new password policy, and an error is added to the model state, as shown in Figure 13-7.
我用PasswordValidator類指定了一個(gè)策略,它要求至少6個(gè)字符,并混用大小字符。通過啟動(dòng)應(yīng)用程序,便可以看到該口令策略的運(yùn)用情況,導(dǎo)航到/Admin/Index網(wǎng)址,點(diǎn)擊Create按鈕,并嘗試創(chuàng)建一個(gè)有口令加密的賬號(hào)。若口令不滿足這一新策略,便會(huì)在模型狀態(tài)中添加一條錯(cuò)誤消息,如圖13-7所示。

圖13-7

Figure 13-7. Reporting an error when validating a password
圖13-7. 驗(yàn)證口令時(shí)報(bào)告的錯(cuò)誤

Implementing a Custom Password Validator
實(shí)現(xiàn)自定義口令驗(yàn)證器

The built-in password validation is sufficient for most applications, but you may need to implement a custom policy, especially if you are implementing a corporate line-of-business application where complex password policies are common. Extending the built-in functionality is done by deriving a new class from PasswordValidatator and overriding the ValidateAsync method. As a demonstration, I added a class file called CustomPasswordValidator.cs in the Infrastructure folder and used it to define the class shown in Listing 13-15.
內(nèi)建的口令驗(yàn)證對(duì)于大多數(shù)應(yīng)用程序足夠了,但你可能需要實(shí)現(xiàn)自定義的策略,尤其是在你實(shí)現(xiàn)一個(gè)公司的在線業(yè)務(wù)應(yīng)用程序時(shí),往往需要復(fù)雜的口令策略。這種對(duì)內(nèi)建功能進(jìn)行擴(kuò)展的做法是,從PasswordValidatator派生一個(gè)新類,并重寫ValidateAsync方法。作為一個(gè)演示,我在Infrastructure文件夾中添加了一個(gè)類文件,名稱為CustomPasswordValidator.cs,如清單13-15所示。

Listing 13-15. The Contents of the CustomPasswordValidator.cs File
清單13-15. CustomPasswordValidator.cs文件的內(nèi)容

using System.Linq;using System.Threading.Tasks;using Microsoft.AspNet.Identity; 
namespace Users.Infrastructure { public class CustomPasswordValidator : PasswordValidator {
public override async Task<IdentityResult> ValidateAsync(string pass) { IdentityResult result = await base.ValidateAsync(pass); if (pass.Contains("12345")) { var errors = result.Errors.ToList(); errors.Add("Passwords cannot contain numeric sequences"); result = new IdentityResult(errors); } return result; } }}

I have overridden the ValidateAsync method and call the base implementation so I can benefit from the built-in validation checks. The ValidateAsync method is passed the candidate password, and I perform my own check to ensure that the password does not contain the sequence 12345. The properties of the IdentityResult class are read-only, which means that if I want to report a validation error, I have to create a new instance, concatenate my error with any errors from the base implementation, and pass the combined list as the constructor argument. I used LINQ to concatenate the base errors with my custom one.
這里重寫了ValidateAsync方法,并調(diào)用了基實(shí)現(xiàn),因此能夠受益于內(nèi)建的驗(yàn)證檢查。給ValidateAsync方法傳遞了申請(qǐng)的口令,然后執(zhí)行了自己的檢查,確保口令中不含數(shù)據(jù)序列12345。IdentityResult類的屬性是只讀的,這意味著,如果想報(bào)告驗(yàn)證錯(cuò)誤,則必須創(chuàng)建一個(gè)新實(shí)例,把這些錯(cuò)誤與基實(shí)現(xiàn)的錯(cuò)誤聯(lián)系在一起,并將這種組合而成的列表作為構(gòu)造器參數(shù)進(jìn)行傳遞。為了將基實(shí)現(xiàn)的錯(cuò)誤與自定義錯(cuò)誤聯(lián)系起來,我使用了LINQ。

Listing 13-16 shows the application of my custom password validator in the AppUserManager class.
清單13-6顯示了在AppUserManager類中使用自定義口令驗(yàn)證器的應(yīng)用程序。

Listing 13-16. Applying a Custom Password Validator in the AppUserManager.cs File
清單13-16. 在AppUserManager.cs文件中運(yùn)用自定義口令驗(yàn)證器

using Microsoft.AspNet.Identity;using Microsoft.AspNet.Identity.EntityFramework;using Microsoft.AspNet.Identity.Owin;using Microsoft.Owin;using Users.Models; 
namespace Users.Infrastructure { public class AppUserManager : UserManager<AppUser> {
public AppUserManager(IUserStore<AppUser> store) : base(store) { }
public static AppUserManager Create(IdentityFactoryOptions<AppUserManager> options, IOwinContext context) {
AppIdentityDbContext db = context.Get<AppIdentityDbContext>(); AppUserManager manager = new AppUserManager( new UserStore<AppUser>(db));
manager.PasswordValidator = new CustomPasswordValidator { RequiredLength = 6, RequireNonLetterOrDigit = false, RequireDigit = false, RequireLowercase = true, RequireUppercase = true };
return manager; } }}

To test the custom password validation, try to create a new user account with the password secret12345. This will break two of the validation rules—one from the built-in validator and one from my custom implementation. Error messages for both problems are added to the model state and displayed when the Create button is clicked, as shown in Figure 13-8.
為了測(cè)試自定義口令驗(yàn)證器,嘗試創(chuàng)建一個(gè)以口令密碼為12345的新用戶賬號(hào)。這會(huì)打破兩條規(guī)則——一條來自于內(nèi)建驗(yàn)證器,一條來自于自定義實(shí)現(xiàn)。兩個(gè)錯(cuò)誤的錯(cuò)誤消息被添加到了模型狀態(tài),并在點(diǎn)擊Create按鈕時(shí)被顯示出來,如圖13-8所示。

圖13-8

Figure 13-8. The effect of a custom password validation policy
圖13-8. 自定義口令驗(yàn)證策略的效果

13.3.4 Validating User Details
13.3.4 驗(yàn)證用戶細(xì)節(jié)

More general validation can be performed by creating an instance of the UserValidator class and using the properties it defines to restrict other user property values. Table 13-8 describes the UserValidator properties.
還可以執(zhí)行更一般的驗(yàn)證,辦法是創(chuàng)建UserValidator類的實(shí)例,并使用它所定義的屬性,以限制用戶其他屬性的值。表13-8描述了UserValidator的屬性。

Table 13-8. The Properties Defined by the UserValidator Class
表13-8. UserValidator類所定義的屬性
Name
名稱
Description
描述
AllowOnlyAlphanumericUserNamesWhen true, usernames can contain only alphanumeric characters.
當(dāng)為true時(shí),用戶名只能含有字母數(shù)字字符
RequireUniqueEmailWhen true, e-mail addresses must be unique.
當(dāng)為true時(shí),郵件地址必須唯一

Performing validation on user details is done by creating an instance of the UserValidator class and assigning it to the UserValidator property of the user manager class within the Create method that OWIN uses to create instances. Listing 13-17 shows an example of using the built-in validator class.
對(duì)用戶細(xì)節(jié)執(zhí)行驗(yàn)證的做法是創(chuàng)建UserValidator類實(shí)例,并在OWIN用來創(chuàng)建實(shí)例的Create方法中,將它賦給用戶管理器類的UserValidator屬性。清單13-17演示了使用內(nèi)建驗(yàn)證器類的一個(gè)示例。

Listing 13-17. Using the Built-in user Validator Class in the AppUserManager.cs File
清單13-17. 在AppUserManager.cs文件中使用內(nèi)建的驗(yàn)證器類

using Microsoft.AspNet.Identity;using Microsoft.AspNet.Identity.EntityFramework;using Microsoft.AspNet.Identity.Owin;using Microsoft.Owin;using Users.Models; 
namespace Users.Infrastructure { public class AppUserManager : UserManager<AppUser> {
public AppUserManager(IUserStore<AppUser> store) : base(store) { }
public static AppUserManager Create(IdentityFactoryOptions<AppUserManager> options, IOwinContext context) {
AppIdentityDbContext db = context.Get<AppIdentityDbContext>(); AppUserManager manager = new AppUserManager( new UserStore<AppUser>(db));
manager.PasswordValidator = new CustomPasswordValidator { RequiredLength = 6, RequireNonLetterOrDigit = false, RequireDigit = false, RequireLowercase = true, RequireUppercase = true };
manager.UserValidator = new UserValidator<AppUser>(manager) { AllowOnlyAlphanumericUserNames = true, RequireUniqueEmail = true };
return manager; } }}

The UserValidator class takes a generic type parameter that specifies the type of the user class, which is AppUser in this case. Its constructor argument is the user manager class, which is an instance of the user manager class (which is AppUserManager for my application).
UserValidator類有一個(gè)泛型的類型參數(shù),它指定了用戶類的類型,即本示例中的AppUser。它的構(gòu)造器參數(shù)是用戶管理器類,這是用戶管理器類(此應(yīng)用程序中的AppUserManager)的一個(gè)實(shí)例。

The built-in validation support is rather basic, but you can create a custom validation policy by creating a class that is derived from UserValidator. As a demonstration, I added a class file called CustomUserValidator.cs to the Infrastructure folder and used it to create the class shown in Listing 13-18.
內(nèi)建的驗(yàn)證支持是相當(dāng)基本的,但通過創(chuàng)建UserValidator的派生類,可以創(chuàng)建自定義驗(yàn)證策略。作為一個(gè)演示,我在Infrastructure文件夾中添加了一個(gè)名稱為CustomUserValidator.cs的類文件,并用它創(chuàng)建了如清單13-19所示的類。

Listing 13-18. The Contents of the CustomUserValidator.cs File
清單13-18. CustomUserValidator.cs文件的內(nèi)容

using System.Linq;using System.Threading.Tasks;using Microsoft.AspNet.Identity;using Users.Models; 
namespace Users.Infrastructure { public class CustomUserValidator : UserValidator<AppUser> {
public CustomUserValidator(AppUserManager mgr) : base(mgr) { }
public override async Task<IdentityResult> ValidateAsync(AppUser user) { IdentityResult result = await base.ValidateAsync(user); if (!user.Email.ToLower().EndsWith("@example.com")) { var errors = result.Errors.ToList(); errors.Add("Only example.com email addresses are allowed"); result = new IdentityResult(errors); } return result; } }}

The constructor of the derived class must take an instance of the user manager class and call the base implementation so that the built-in validation checks can be performed. Custom validation is implemented by overriding the ValidateAsync method, which takes an instance of the user class and returns an IdentityResult object. My custom policy restricts users to e-mail addresses in the example.com domain and performs the same LINQ manipulation I used for password validation to concatenate my error message with those produced by the base class. Listing 13-19 shows how I applied my custom validation class in the Create method of the AppUserManager class, replacing the default implementation.
這個(gè)派生類的構(gòu)造器必須以用戶管理器類實(shí)例為參數(shù),并調(diào)用基實(shí)現(xiàn),才能夠執(zhí)行內(nèi)建的驗(yàn)證檢查。自定義驗(yàn)證是通過重寫ValidateAsync方法而實(shí)現(xiàn)的,該方法以用戶類實(shí)例為參數(shù),并返回一個(gè)IdentityResult對(duì)象。上述自定義驗(yàn)證策略將用戶的E-mail地址限制在example.com主域內(nèi),并執(zhí)行了與口令驗(yàn)證同樣的LINQ操作,將這里的錯(cuò)誤消息與基類產(chǎn)生的錯(cuò)誤消息關(guān)聯(lián)在一起。清單13-19演示了如何在AppUserManager類的Create方法中運(yùn)用自定義驗(yàn)證類,用以替換默認(rèn)的實(shí)現(xiàn)。

Listing 13-19. Using a Custom User Validation Class in the AppUserManager.cs File
清單13-19. 在AppUserManager.cs文件中使用自定義用戶驗(yàn)證類

using Microsoft.AspNet.Identity;using Microsoft.AspNet.Identity.EntityFramework;using Microsoft.AspNet.Identity.Owin;using Microsoft.Owin;using Users.Models; 
namespace Users.Infrastructure { public class AppUserManager : UserManager<AppUser> {
public AppUserManager(IUserStore<AppUser> store) : base(store) { }
public static AppUserManager Create(IdentityFactoryOptions<AppUserManager> options, IOwinContext context) {
AppIdentityDbContext db = context.Get<AppIdentityDbContext>(); AppUserManager manager = new AppUserManager( new UserStore<AppUser>(db));
manager.PasswordValidator = new CustomPasswordValidator { RequiredLength = 6, RequireNonLetterOrDigit = false, RequireDigit = false, RequireLowercase = true, RequireUppercase = true };
manager.UserValidator = new CustomUserValidator(manager) { AllowOnlyAlphanumericUserNames = true, RequireUniqueEmail = true };

return manager; } }}

You can see the result if you try to create an account with an e-mail address such as [email protected], as shown in Figure 13-9.
如果試圖創(chuàng)建一個(gè)E-mail地址為[email protected]的賬號(hào),便會(huì)看到如圖13-9所示的結(jié)果。

圖13-9

Figure 13-9. An error message shown by a custom user validation policy
圖13-9. 由自定義用戶驗(yàn)證顯示的錯(cuò)誤消息

13.4 Completing the Administration Features
13.4 完成管理特性

I only have to implement the features for editing and deleting users to complete my administration tool. In Listing 13-20, you can see the changes I made to the Views/Admin/Index.cshtml file to target Edit and Delete actions in the Admin controller.
為了完成本例的管理工具,我只需實(shí)現(xiàn)編輯和刪除用戶的特性。在清單13-20中可以看到我對(duì)Views/Admin/Index.cshtml文件所做修改,它們的目標(biāo)是Admin控制器中的EditDelete方法。

Listing 13-20. Adding Edit and Delete Buttons to the Index.cshtml File
清單13-20. 在Index.cshtml文件中添加“Edit”和“Delete”按鈕

@using Users.Models@model IEnumerable<AppUser>@{ ViewBag.Title = "Index"; }<div class="panel panel-primary">    <div class="panel-heading">        User Accounts    </div>    <table class="table table-striped">         <tr><th>ID</th><th>Name</th><th>Email</th><th></th></tr>         @if (Model.Count() == 0) {            <tr><td colspan="4" class="text-center">No User Accounts</td></tr>         } else {            foreach (AppUser user in Model) {                <tr>                    <td>@user.Id</td>                    <td>@user.UserName</td>                    <td>@user.Email</td>                    <td>                        @using (Html.BeginForm("Delete", "Admin",                                    new { id = user.Id })) {                            @Html.ActionLink("Edit", "Edit", new { id = user.Id },                                    new { @class = "btn btn-primary btn-xs" })                            <button class="btn btn-danger btn-xs" type="submit">                                Delete                            </button>                        }                    </td>                 </tr>            }        }    </table></div>@Html.ActionLink("Create", "Create", null, new { @class = "btn btn-primary" })

Tip You will notice that I have put the Html.ActionLink call that targets the Edit action method inside the scope of the Html.Begin helper. I did this solely so that the Bootstrap styles will style both elements as buttons displayed on a single line.
提示:你可能會(huì)注意到,我在Html.Begin輔助器的范圍內(nèi)放入了一個(gè)Html.ActionLink調(diào)用,其目標(biāo)為Edit動(dòng)作方法。這么做純粹是為了讓Bootstrap將兩個(gè)元素的樣式作為按鈕顯示成一行。

13.4.1 Implementing the Delete Feature
13.4.1 實(shí)現(xiàn)Delete特性

The user manager class defines a DeleteAsync method that takes an instance of the user class and removes it from the database. In Listing 13-21, you can see how I have used the DeleteAsync method to implement the delete feature of the Admin controller.
用戶管理器類定義了一個(gè)DeleteAsync方法,它以用戶類實(shí)例為參數(shù),并將其從數(shù)據(jù)庫刪除。在清單13-21中,可以看到如何用DeleteAsync方法來實(shí)現(xiàn)Admin控制器的刪除特性。

Listing 13-21. Deleting Users in the AdminController.cs File
清單13-21. AdminController.cs文件中的刪除用戶

using System.Web;using System.Web.Mvc;using Microsoft.AspNet.Identity.Owin;using Users.Infrastructure;using Users.Models;using Microsoft.AspNet.Identity;using System.Threading.Tasks; 
namespace Users.Controllers { public class AdminController : Controller {
// ...other action methods omitted for brevity... // ...出于簡化,這里忽略了其他方法...
[HttpPost] public async Task<ActionResult> Delete(string id) { AppUser user = await UserManager.FindByIdAsync(id); if (user != null) { IdentityResult result = await UserManager.DeleteAsync(user); if (result.Succeeded) { return RedirectToAction("Index"); } else { return View("Error", result.Errors); } } else { return View("Error", new string[] { "User Not Found" }); } }
private void AddErrorsFromResult(IdentityResult result) { foreach (string error in result.Errors) { ModelState.AddModelError("", error); } }
private AppUserManager UserManager { get { return HttpContext.GetOwinContext().GetUserManager<AppUserManager>(); } } }}

My action method receives the unique ID for the user as an argument, and I use the FindByIdAsync method to locate the corresponding user object so that I can pass it to DeleteAsync method. The result of the DeleteAsync method is an IdentityResult, which I process in the same way I did in earlier examples to ensure that any errors are displayed to the user. You can test the delete functionality by creating a new user and then clicking the Delete button that appears alongside it in the Index view.
該動(dòng)作方法以用戶的唯一ID作為參數(shù),并且使用FindByIdAsync方法定位了相應(yīng)的用戶對(duì)象,以便將該對(duì)象傳遞給DeleteAsync方法。DeleteAsync方法的結(jié)果是一個(gè)IdentityResult,對(duì)它的處理方式與之前示例的方式相同,以確保將任何錯(cuò)誤都顯示給用戶。為了測(cè)試這個(gè)刪除功能,可以創(chuàng)建一個(gè)新的用戶,然后在Index視圖中點(diǎn)擊出現(xiàn)在該用戶旁邊的“Delete”按鈕。

There is no view associated with the Delete action, so to display any errors I created a view file called Error.cshtml in the Views/Shared folder, the contents of which are shown in Listing 13-22.
Delete動(dòng)作沒有相關(guān)聯(lián)的視圖,因此,為了將錯(cuò)誤顯示出來,我在Views/Shared文件夾中創(chuàng)建了一個(gè)視圖文件,名稱為Error.cshtml,其內(nèi)容如清單13-22所示。

Listing 13-22. The Contents of the Error.cshtml File
清單13-22. Error.cshtml文件的內(nèi)容

@model IEnumerable<string>@{ ViewBag.Title = "Error";}<div class="alert alert-danger">    @switch (Model.Count()) {        case 0:            @: Something went wrong. Please try again            break;        case 1:            @Model.First();            break;        default:            @: The following errors were encountered:                <ul>                    @foreach (string error in Model) {                        <li>@error</li>                    }                </ul>            break;    }</div>@Html.ActionLink("OK", "Index", null, new { @class = "btn btn-default" })

Tip I put this view in the Views/Shared folder so that it can be used by other controllers, including the one I create to manage roles and role membership in Chapter 14.
提示:我將此視圖放在Views/Shared文件夾中,是為了其他控制器也能夠使用它,包括第14章為了管理角色及其成員要?jiǎng)?chuàng)建的控制器。

13.4.2 Implementing the Edit Feature
13.4.2 實(shí)現(xiàn)Edit特性

To complete the administration tool, I need to add support for editing the e-mail address and password for a user account. These are the only properties defined by users at the moment, but I'll show you how to extend the schema with custom properties in Chapter 15. Listing 13-23 shows the Edit action methods that I added to the Admin controller.
為了完成用戶管理工具,我需要為編輯用戶賬號(hào)的E-mail地址和口令的支持。此時(shí)這些是用戶定義僅有的屬性,不過到第15章時(shí),將演示如何擴(kuò)展數(shù)據(jù)庫架構(gòu),使用戶帶有自定義屬性。清單13-23顯示了添加到Admin控制器中的Edit動(dòng)作方法。

Listing 13-23. Adding the Edit Actions in the AdminController.cs File
清單13-23. 在AdminController.cs文件中添加Edit動(dòng)作

using System.Web;using System.Web.Mvc;using Microsoft.AspNet.Identity.Owin;using Users.Infrastructure;using Users.Models;using Microsoft.AspNet.Identity;using System.Threading.Tasks; 
namespace Users.Controllers { public class AdminController : Controller {
// ...other action methods omitted for brevity... // ...出于簡化,這里忽略了其他動(dòng)作方法...
public async Task<ActionResult> Edit(string id) { AppUser user = await UserManager.FindByIdAsync(id); if (user != null) { return View(user); } else { return RedirectToAction("Index"); } }
[HttpPost] public async Task<ActionResult> Edit(string id, string email, string password) { AppUser user = await UserManager.FindByIdAsync(id); if (user != null) { user.Email = email; IdentityResult validEmail = await UserManager.UserValidator.ValidateAsync(user); if (!validEmail.Succeeded) { AddErrorsFromResult(validEmail); } IdentityResult validPass = null; if (password != string.Empty) { validPass = await UserManager.PasswordValidator.ValidateAsync(password); if (validPass.Succeeded) { user.PasswordHash = UserManager.PasswordHasher.HashPassword(password); } else { AddErrorsFromResult(validPass); } } if ((validEmail.Succeeded && validPass == null) || ( validEmail.Succeeded && password != string.Empty && validPass.Succeeded)) { IdentityResult result = await UserManager.UpdateAsync(user); if (result.Succeeded) { return RedirectToAction("Index"); } else { AddErrorsFromResult(result); } } } else { ModelState.AddModelError("", "User Not Found"); } return View(user); }

private void AddErrorsFromResult(IdentityResult result) { foreach (string error in result.Errors) { ModelState.AddModelError("", error); } }
private AppUserManager UserManager { get { return HttpContext.GetOwinContext().GetUserManager<AppUserManager>(); } } }}

The Edit action targeted by GET requests uses the ID string embedded in the Index view to call the FindByIdAsync method in order to get an AppUser object that represents the user.
由GET請(qǐng)求作為目標(biāo)的Edit動(dòng)作使用Index視圖中的ID字符串調(diào)用FindByIdAsync方法,目的是獲得表示該用戶的AppUser對(duì)象。

The more complex implementation receives the POST request, with arguments for the user ID, the new e-mail address, and the password. I have to perform several tasks to complete the editing operation.
那個(gè)較復(fù)雜的實(shí)現(xiàn)接收POST請(qǐng)求,以用戶ID、新E-mail地址以及口令為參數(shù)。我必須執(zhí)行幾個(gè)任務(wù)才能完成這種編輯操作。

The first task is to validate the values I have received. I am working with a simple user object at the moment—although I'll show you how to customize the data stored for users in Chapter 15—but even so, I need to validate the user data to ensure that I don't violate the custom policies defined in the “Validating User Details” and “Validating Passwords” sections. I start by validating the e-mail address, which I do like this:
第一個(gè)任務(wù)是驗(yàn)證接收到的值。此時(shí)使用的是一個(gè)簡單的用戶對(duì)象——盡管第15章將演示如何自定義存儲(chǔ)用戶的數(shù)據(jù)——但即使如此,還是需要驗(yàn)證用戶數(shù)據(jù),以確保不會(huì)與“驗(yàn)證用戶細(xì)節(jié)”和“驗(yàn)證口令”小節(jié)中定義的自定義策略相沖突。首先驗(yàn)證E-mail地址,做法如下:

...user.Email = email;IdentityResult validEmail = await UserManager.UserValidator.ValidateAsync(user); if (!validEmail.Succeeded) {    AddErrorsFromResult(validEmail);}...

Tip Notice that I have to change the value of the Email property before I perform the validation because the ValidateAsync method only accepts instances of the user class.
提示:注意,在執(zhí)行驗(yàn)證之前,必須修改Email屬性的值,因?yàn)?span id="70qarhlowxag" class="code">ValidateAsync方法只接收用戶類的實(shí)例。

The next step is to change the password, if one has been supplied. ASP.NET Identity stores hashes of passwords, rather than the passwords themselves—this is intended to prevent passwords from being stolen. My next step is to take the validated password and generate the hash code that will be stored in the database so that the user can be authenticated (which I demonstrate in Chapter 14).
下一個(gè)步驟是在已提供口令時(shí),修改用戶口令。ASP.NET Identity存儲(chǔ)的是口令的哈希值,而不是口令本身——目的是防止口令被竊取。我的下一個(gè)步驟是取得這個(gè)已驗(yàn)證的口令,并生成將被存儲(chǔ)到數(shù)據(jù)庫中的哈希碼,以便讓用戶能夠被認(rèn)證,這將在第14章演示。

Passwords are converted to hashes through an implementation of the IPasswordHasher interface, which is obtained through the AppUserManager.PasswordHasher property. The IPasswordHasher interface defines the HashPassword method, which takes a string argument and returns its hashed value, like this:
將口令轉(zhuǎn)換成哈希值是通過IPasswordHasher接口的實(shí)現(xiàn)來做的,該接口實(shí)現(xiàn)是通過AppUserManager.PasswordHasher屬性獲得的。IPasswordHasher接口定義了HashPassword方法,它以一個(gè)字符串為參數(shù),返回該字符串的哈希值,如下所示:

...if (password != string.Empty) {    validPass = await UserManager.PasswordValidator.ValidateAsync(password);     if (validPass.Succeeded) {        user.PasswordHash = UserManager.PasswordHasher.HashPassword(password);     } else {        AddErrorsFromResult(validPass);    }}...

Changes to the user class are not stored in the database until the UpdateAsync method is called, like this:
對(duì)用戶類的修改直到調(diào)用UpdateAsync方法時(shí),才會(huì)存儲(chǔ)到數(shù)據(jù)庫中,如下所示:

...if ((validEmail.Succeeded && validPass == null)        || ( validEmail.Succeeded && password != string.Empty &&        validPass.Succeeded)) {    IdentityResult result = await UserManager.UpdateAsync(user);     if (result.Succeeded) {        return RedirectToAction("Index");    } else {        AddErrorsFromResult(result);    }}...

1. Creating the View
1. 創(chuàng)建視圖

The final component is the view that will render the current values for a user and allow new values to be submitted to the controller. Listing 13-24 shows the contents of the Views/Admin/Edit.cshtml file.
最后一個(gè)組件是渲染當(dāng)前用戶值并將新值遞交給控制器的視圖。清單13-24顯示了Views/Admin/Edit.cshtml文件的內(nèi)容。

Listing 13-24. The Contents of the Edit.cshtml File
清單13-24. Edit.cshtml文件的內(nèi)容

@model Users.Models.AppUser@{ ViewBag.Title = "Edit"; }@Html.ValidationSummary(false)<h2>Edit User</h2> 
<div class="form-group"> <label>Name</label> <p class="form-control-static">@Model.Id</p></div>@using (Html.BeginForm()) { @Html.HiddenFor(x => x.Id) <div class="form-group"> <label>Email</label> @Html.TextBoxFor(x => x.Email, new { @class = "form-control" }) </div> <div class="form-group"> <label>Password</label> <input name="password" type="password" class="form-control" /> </div> <button type="submit" class="btn btn-primary">Save</button> @Html.ActionLink("Cancel", "Index", null, new { @class = "btn btn-default" })}

There is nothing special about the view. It displays the user ID, which cannot be changed, as static text and provides a form for editing the e-mail address and password, as shown in Figure 13-10. Validation problems are displayed in the validation summary section of the view, and successfully editing a user account will return to the list of accounts in the system.
該視圖沒什么特別的。它將用戶ID顯示成靜態(tài)文本,這是不能修改的,并且提供了一個(gè)對(duì)E-mail地址和口令進(jìn)行編輯的表單,如圖13-10所示。驗(yàn)證問題會(huì)顯示在視圖的驗(yàn)證摘要處,當(dāng)成功編輯一個(gè)用戶賬號(hào)時(shí),會(huì)返回到系統(tǒng)的賬號(hào)列表。

圖13-10

Figure 13-10. Editing a user account
圖13-10. 編輯用戶賬號(hào)

13.5 Summary
13.5 小結(jié)

In this chapter, I showed you how to create the configuration and classes required to use ASP.NET Identity and demonstrated how they can be applied to create a user administration tool. In the next chapter, I show you how to perform authentication and authorization with ASP.NET Identity.
在本章中,我演示了如何創(chuàng)建使用ASP.NET Identity所需的配置和類,并且演示了如何運(yùn)用它們來創(chuàng)建用戶管理工具。下一章將演示如何用ASP.NET Identity執(zhí)行認(rèn)證與授權(quán)。


看完此文如果覺得有所收獲,請(qǐng)給個(gè)推薦
你的推薦是我繼續(xù)下去的動(dòng)力,也會(huì)讓更多人關(guān)注并獲益,這也是你的貢獻(xiàn)。


發(fā)表評(píng)論 共有條評(píng)論
用戶名: 密碼:
驗(yàn)證碼: 匿名發(fā)表
主站蜘蛛池模板: 色婷婷一区二区三区 | 精品国产一区二区三区四区阿崩 | 意大利av在线 | 亚洲午夜精品视频 | 久久国产精品系列 | a视频在线免费观看 | 91免费视频版 | 亚洲尻逼视频 | 久久草在线视频国产 | 国产99久久精品一区二区300 | 91成人在线免费 | 久久久久久久久久久久久久久久久久 | 久久精品9 | 久久精品视频黄色 | xxxx8| 国产精品美女一区二区 | 九一免费国产 | 国产精品视频免费在线观看 | 精品国产一级毛片 | 丰满年轻岳中文字幕一区二区 | 男人的天堂视频网站 | 欧美一级毛片一级毛片 | 免费看成年人网站 | 日本黄色大片免费观看 | 久久国产一二区 | 国产精品久久久久久久久久iiiii | 最新一级毛片 | 中文亚洲视频 | 免费国产自久久久久三四区久久 | 免费在线观看成人av | 久草视频福利在线观看 | 国产成人观看 | 主播粉嫩国产在线精品 | a级毛片免费观看在线播放 日本aaa一级片 | 亚洲成人精品视频 | 欧美性色大片 | 欧美亚洲综合在线 | 精精国产xxxx视频在线野外 | 美女视频在线观看黄 | 久久成人在线观看 | 精品一区二区三区免费看 |