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

首頁 > 學院 > 開發設計 > 正文

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

2019-11-14 14:15:25
字體:
來源:轉載
供稿:網友

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

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應用程序中管理用戶的一個新的API。近年來用戶管理的基石一直是ASP.NET的Membership。Membership在2005年推出時還算是一個合理的選擇,但目前看來已經嚴重過時了。它最大的限制是用以存儲數據的架構(Database Schema)只能使用SQL Server,而且難以擴展,除非重新實現大量的提供器類。其數據架構本身也過于復雜,使之比理論上還要難以實現修改。

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.
在發布Identity之前,微軟曾做過兩次改善Membership的嘗試。第一個嘗試稱為Simple Membership(簡單成員),它降低了數據庫架構的復雜性,并使之易于定制用戶數據,但仍然需要關系型存儲模型。第二個嘗試是ASP.NET的Universal Providers(通用提供器),第10章在為會話數據建立SQL Server存儲庫時曾用過它。Universal Providers的好處是,這些提供器使用了Entity Framework的Code First特性,能夠自動地創建數據庫架構,使之能夠在架構管理工具無法訪問的情況下,例如Azure云服務,也能夠創建數據庫。但即使有了改善,其依賴于關系型數據以及難以定制等根本問題仍然存在。

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.
為了解決這兩個問題并提供一個更現代的用戶管理平臺,微軟用Identity取代了Membership。正如將在本章以及第14、15章所了解到的,ASP.NET Identity靈活且可擴展,但它仍不成熟,你在一些更成熟的系統中能夠獲得的特性,可能需要超常的工作量。

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.
微軟已經完全彌補了Membership的不靈活性,使Identity十分開放和廣泛適應,幾乎能夠以任何方式進行使用——只要你有時間有能力做出你所需要的實現即可。

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.
在本章中,我會演示建立ASP.NET Identity的過程,并創建一個簡單的用戶管理工具,用以管理存儲在數據庫中的個別用戶賬號。

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還支持其他類型的用戶賬號,例如用Active Directory(活動目錄)存儲的賬號,但我不會對其進行描述,因為這種賬號通常不會用于公司的外部(這種場合的Active Directory實現往往很復雜,我難以提供有用的通用示例)。

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章中,我將演示如何用這些用戶賬號進行認證與授權,第15章將演示如何進入高級論題,運用一些高級技術。表13-1是本章概要。

Table 13-1. Chapter Summary
表13-1. 本章概要
Problem
問題
Solution
解決方案
Listing
清單號
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文件中定義一個鏈接字符串和一個OWIN啟動類
1–4
Prepare to use ASP.NET Identity.
使用ASP.NET Identity的準備
Create classes that represent the user, the user manager, the database context, and the OWIN start class.
創建表示用戶、用戶管理器、數據庫上下文的類,以及OWIN類
5–8
Enumerate user accounts.
枚舉用戶賬號
Use the Users property defined by the user manager class.
使用由用戶管理器類定義的Users屬性
9, 10
Create user accounts.
創建用戶賬號
Use the CreateAsync method defined by the user manager class.
使用由用戶管理器類定義的CreateAsync方法
11–13
Enforce a passWord policy.
強制口令策略
Set the PasswordValidator property defined by the user manager class, either using the built-in PasswordValidator class or using a custom derivation.
設置由用戶管理器類定義的PasswordValidator屬性,既可以使用內建的PasswordValidator類,也可以使用自定義的派生類。
14–16
Validate new user accounts.
驗證新的用戶賬號
Set the UserValidator property defined by the user manager class, either using the built-in UserValidator class or using a custom derivation.
設置由用戶管理器類定義的UserValidator屬性,既可以使用內建的UserValidator類,也可以使用自定義的派生類。
17–19
Delete user accounts.
刪除用戶賬號
Use the DeleteAsync method defined by the user manager class.
使用由用戶管理器定義的DeleteAsync方法
20–22
Modify user accounts.
修改用戶賬號
Use the UpdateAsync method defined by the user manager class.
使用由用戶管理器類定義的UpdateAsync方法
23–24

13.1 Preparing the Example Project
13.1 準備示例項目

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:
本章根據本書一直采用的同樣步驟創建了一個名稱為Users的項目。在創建過程中選擇了“Empty(空)” 模板,并在“Add the folders and references(添加文件夾和引用)”中選中了“MVC”復選框。本章將使用Bootstrap來設置視圖的樣式,因此在Visual Studio的“Package Manager Console(包管理器控制臺)”中輸入以下命令,并按回車,下載并安裝這個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.
我創建了Home控制器,以作為本章示例的焦點。該控制器的定義如清單13-1所示。此控制器將用來描述用戶賬號的細節和數據,Index動作方法通過View方法給默認視圖傳遞了一個字典值。

Listing 13-1. The Contents of the HomeController.cs File
清單13-1. HomeController.cs文件的內容

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動作方法,并從彈出菜單選擇“Add View(添加視圖)”,我創建了一個視圖。將“View Name(視圖名稱)”設置為“Index”,并將“Template(模板)”設置為“空(無模型)”。與前面幾章的示例不同,本章希望使用一個通用的布局,于是選中了“Use a Layout Page(使用布局頁面)”復選框。點擊“Add(添加)”按鈕后,Visual Studio創建了Views/Shared/_Layout.cshtmlViews/Home/Index.cshtml文件。清單13-2顯示了_Layout.cshtml文件的內容。

Listing 13-2. The Contents of the _Layout.cshtml File
清單13-2. _Layout.cshtml文件的內容

<!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文件的內容。

Listing 13-3. The Contents of the Index.cshtml File
清單13-3. Index.cshtml文件的內容

@{    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.
為了測試該應用程序示例能夠工作,從Visual Studio的“Debug(調試)”菜單中選擇“Start Debugging(啟動調試)”,并導航到/Home/Index網址,便可以看到如圖13-1所示的結果。

圖13-1

Figure 13-1. Testing the example application
圖13-1. 測試示例應用程序

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.
對于大多數ASP.NET開發者而言,Identity將是第一個暴露給OWIN(Open Web Interface for .NET——.NET開放Web接口)的組件。OWIN是一個將Web應用程序從托管它的環境中獨立出來的抽象層。其思想是這個獨立出來的抽象層能夠使ASP.NET技術堆棧有更大的創新,使托管ASP.NET應用程序的環境有更多的靈活性,并可以是輕量級的服務器架構。

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是一個開放標準(參閱http://owin.org/spec/owin-1.0.html)。微軟已經創建了Katana項目,該項目是OWIN標準的實現,并提供了一組提供了Web應用程序所需功能的組件。讓微軟感興趣的是OWIN/Katana將ASP.NET技術堆棧從.NET框架的其余部分獨立了出來,這帶來了更大的修改速率(譯者對OWIN還沒太了解,這可能是指若按照OWIN的方式開發應用程序,可以使應用程序更易于修改——譯者注)。

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開發人員為他們的應用程序選擇所需的服務,而不是像現在這樣單純地使用ASP.NET平臺。個別服務——在OWIN術語中稱為“Middleware(中間件)”——可能會有不同的開發速率,而開發人員將能夠在不同的服務提供商之間進行選擇,而不是被綁定在微軟實現上。

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有很多喜歡發展的方向,但它仍處于早期時期,還需要一段時間才能成為ASP.NET應用程序的完整平臺。在我編寫本書的時候,它已可以在不需要System.Web命名空間或者IIS處理請求的情況下,建立Web API和SignalR應用程序了。MVC框架需要標準的ASP.NET平臺,因此在一段時間內仍將沿用現在的做法。

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平臺以及IIS不會消失。微軟已經清楚地看到,OWIN最有吸引力的一個方面是開發者具有了更大的靈活性,中間組件可以由IIS來托管,而Katana項目已經實現了對System.Web命名空間的支持。OWIN和Katana不是ASP.NET的終結——而是預示著一種變革,微軟讓開發人員能夠在ASP.NET應用程序的編譯和執行方面更加靈活。

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中間件而交付的第一個主要的ASP.NET組件,但這不是最后一個。微軟已經保證,Web API和SignalR的最新版本不會依賴于System.Web命名空間,這意味著,打算交叉使用ASP.NET家族技術的任何組件,都必須通過OWIN實行交付。關于OWIN,在我的Expert ASP.NET Web API 2 for MVC Developers一書中有更詳細的論述,該書已由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在一段時間內還不會對MVC框架開發人員發生重大沖擊,但變化正在發生——其中之一便是ASP.NET Identity要作為OWIN中間件而實現。這種情況不太理想,因為這意味著,MVC框架的應用程序為了使用Identity,必須將OWIN和傳統的ASP.NET平臺混搭在一起,這太煩瑣了,在你理解了基本概念并且知道如何建立OWIN(以下幾小節演示)之后,便會明白。

13.2.1 Creating the ASP.NET Identity Database
13.2.1 創建ASP.NET Identity數據庫

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架構,但關系型存儲仍是默認的,而且是最簡單的選擇,這也是本章中將使用的形式。雖然近年來出現了NoSQL運動勢頭,但關系型數據庫仍然是主要的存儲選擇,而且大多數開發團隊都有良好理解。

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特性自動地創建它的數據架構(Schema),但我仍然需要創建一個用來放置此數據架構以及用戶數據的數據庫,就像第10章所做的那樣,當時為會話狀態數據創建了數據庫(用來管理數據庫的通用提供器同樣使用了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特性的工作機制。

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特性來創建數據庫。要記住的是,localdb是包含在Visual Studio之中的,而且是一個簡化版的SQL Server,能夠讓開發者方便地創建和使用數據庫。

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對象資源管理器)”,并在所出現的窗口中右擊“SQL Server”對象。從彈出菜單選擇“Add SQL Server(添加SQL Server)”,如圖13-2所示。

圖13-2

Figure 13-2. Creating a new database connection
圖13-2. 創建一個新的數據庫連接

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(連接到服務器)”對話框,將服務器名稱設置為(localdb)/v11.0,選擇“Windows Authentication(Windows認證)”選項,點擊“Connect(連接)”按鈕。這將建立一個數據庫連接,并顯示在“SQL Server對象資源管理器”窗口中。展開這個新項,右擊“Databases(數據庫)”,并從彈出菜單選擇“Add New Database(添加新數據庫)”,如圖13-3所示。

圖13-3

Figure 13-3. Adding a new database
圖13-3. 添加新數據庫

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(數據庫名稱)”選項設置為“IdentityDb”,不用改變“Database Location(數據庫位置)”的值,點擊OK按鈕創建此數據庫。這個新數據庫將出現在“SQL Server對象資源管理器”中“SQL連接”的“數據庫”小節中。

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包發布的,因此易于將其安裝到任何項目。請在“Package Manager Console(包管理器控制臺)”中輸入以下命令:

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能夠創建一些使用Identity API的項目,這種項目以“泛型用戶賬號管理配置”進行配置。你可以給項目添加一些模板和代碼,只需在創建項目時選擇MVC模板,并將認證選項設置為Individual User Accounts(個別用戶賬號)。我沒有使用這種模板,因為我發現它們太普通,也太混亂,而且我喜歡對我項目中的內容和配置有直接的控制。我建議你也這么做,這不僅讓你能夠對重要特性的工作機制獲得更好的理解,而且,考察模板如何執行常規任務也是有趣的。

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.)
為了做好項目使用ASP.NET Identity的準備,需要在Web.config文件中做兩處修改。第一處是連接字符串,它描述了我在上一小節中創建的數據庫。第二處修改是定義一個應用程序設置,它命名對OWIN中間件進行初始化的類,并將它用于配置Identity。清單顯示了對Web.config文件的修改(第9章已經解釋過連接字符串和應用程序設置)。

Listing 13-4. Preparing the Web.config File for ASP.NET Identity
清單13-4. 為ASP.NET Identity準備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的值放在一行中,這里把它給斷開了,是為了讓清單適應頁面,但ASP.NET要求是單一無斷行的字符串。如果有疑問,請下載本書的伴隨代碼,下載地址: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定義了它自己的應用程序啟動模型,與第3章所描述的全局應用程序類是分開的。上述的應用程序設置,名稱為owin:AppStartup,指定了一個應用程序啟動時OWIN會進行實例化的類,目的是接受它的配置。

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.
提示:注意,我已經在連接字符串中將MultipleActiveResultSets屬性(多活動結果集)設置為true,這樣可以從同時讀取多個查詢形成結果,第14章依賴于這種方式,那時會演示如何根據角色成員授權訪問動作方法。

13.2.4 Creating the Entity Framework Classes
13.2.4 創建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.
如果你曾在項目使用過Membership,可能會感覺奇怪,ASP.NET Identity需要的初始化準備怎么這么多。在ASP.NET Identity中具備了Membership所缺乏的可擴展性,但其代價是需要創建一組實現類,由Entity Framework用于管理數據庫。在以下小節中,我將演示如何創建所需要的這些類,以便讓Entity Framework把它們用于ASP.NET Identity的存儲系統。

1. Creating the User Class
1. 創建用戶類

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.
第一個要定義的類是表現一個用戶的類,我將它稱為“User Class(用戶類)”。這個用戶類派生于IdentityUser,它是在Microsoft.AspNet.Identity.EntityFramework命名空間中定義的。IdentityUser提供了基本的用戶表示,可以通過在它派生的類中添加屬性的辦法,對這個類進行擴展,我會在第15章中對此進行描述。表13-2列出了IdentityUser所定義的內建屬性(現在流行將“內建”說成“內置”,其實這兩者在含義上有很大差別,“內建”完全是自己創建的,而“內置”有可能是別人做的東西拿來放入其中的——譯者注),本章將使用這些屬性。

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
返回用戶的聲明集合,關于聲明(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
返回哈希格式的用戶口令,在“實現Edit特性”中會用到它
RolesReturns the collection of roles that the user belongs to, which I describe in Chapter 14
返回用戶所屬的角色集合,將在第14章描述
PhoneNumberReturns the user's phone number
返回用戶的電話號碼
SecurityStampReturns a value that is changed when the user identity is altered, such as by a password change
返回變更用戶標識時被修改的值,例如被口令修改的值
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專用的具體實現。例如,IdentityUser便是IUser接口的實現。我會使用這些具體類,因為我要依靠Entity Framework在數據庫中存儲我的用戶數據,等到ASP.NET Identity變得成熟時,你可能會期望看到這些接口的其他實現,它們使用了不同的存儲機制(當然,大多數項目仍然會使用Entity Framework,因為它來自于微軟)。

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.
目前最重要的是這個IdentityUser類只提供了對用戶基本信息的訪問:用戶名、E-mail、電話、哈希口令、角色成員等等。如果希望存儲用戶的各種附加信息,就需要在IdentityUser派生的類上添加屬性,并將它用于表示應用程序中的用戶,第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.
為了創建應用程序中的用戶類,我在Models文件夾中創建了一個類文件,名稱為AppUserModels.cs(注意,這個文件名稱錯了,應當是AppUser.cs),并用它創建了AppUser類,這個類如清單13-5所示。

Listing 13-5. The Contents of the AppUser.cs File
清單13-5. AppUser.cs文件的內容

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章會再次討論這個類,那時會演示如何添加應用程序專用的用戶數據屬性。

2. Creating the Database Context Class
2. 創建數據庫上下文類

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.
下一個步驟是創建Entity Framework數據庫的上下文,用于對AppUser類進行操作。這可以用Code First特性來創建和管理數據架構,并對數據庫所存儲的數據進行訪問。這個上下文類派生于IdentityDbContext<T> ,其中的T是用戶類(即此例中的AppUser)。我在項目中創建了一個文件夾,名稱為Infrastructure,并在其中添加了一個類文件,名稱為AppIdentityDbContext.cs,其內容如清單13-6所示。

Listing 13-6. The Contents of the AppIdentityDbContext.cs File
清單13-6. AppIdentityDbContext.cs文件的內容

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.
這個AppIdentityDbContext類的構造器調用了它的基類,其參數是連接字符串的名稱,IdentityDb,用于與數據庫連接。這是讓清單13-4定義的連接字符串與ASP.NET Identity聯結起來的方法。

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.
這個AppIdentityDbContext類還定義了一個靜態的構造器,它使用Database.SetInitializer方法指定了一個種植數據庫的類(種植數據庫的含義是往數據庫中植入數據的意思,說穿了,就是用一些數據對數據庫進行初始化——譯者注),當通過Entity Framework的Code First特性第一次創建數據庫架構時,會用到這個類。這個種植類叫做IdentityDbInit,而且我已經提供了一個類,創建了一個占位符,后面可以回過頭來在PerformInitialSetup方法中添加語句,就可以種植數據庫了。第14章將演示如何種植數據庫。

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類定義了一個Create方法。這是由OWIN在必要時創建類實例的辦法,這個由OWIN使用的類將在“創建啟動類”中進行描述。

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.
注:如果對這些類的意義無法理解,不用擔心。如果不熟悉Entity Framework,我建議你將它視為是某種黑箱事物。一旦基礎構建塊就緒——而且對本章的這些代碼進行拷貝——那么就幾乎不需要編輯它們了。

3. Creating the User Manager Class
3. 創建用戶管理器類

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(用戶管理器)”,用來管理用戶類實例。用戶管理器類必須派生于UserManager<T> ,其中T是用戶類。這個UserManager<T> 類并非是專用于Entity Framework的,而且它提供了很多通用特性,用以創建用戶并對用戶數據進行操作。表13-3列出了UserManager<T> 類為管理用戶而定義的基本方法和操作。還有一些其他方法,這里并未全部列出來,我會在適當的情形下對它們進行描述,那時會介紹管理用戶數據的不同方式。

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.
創建一個不帶口令的新用戶,參見第15章示例
CreateAsync(user, pass)Creates a new user with the specified password. See the “Creating Users” section.
創建一個帶有指定口令的新用戶,參見“創建用戶”
DeleteAsync(user)Deletes the specified user. See the “Implementing the Delete Feature” section.
刪除指定用戶,參見“實現Delete特性”
FindAsync(user, pass)Finds the object that represents the user and authenticates their password. See Chapter 14 for details of authentication.
查找代表該用戶的對象,并認證其口令,詳見第14章的認證細節
FindByIdAsync(id)Finds the user object associated with the specified ID. See the “Implementing the Delete Feature” section.
查找與指定ID相關聯的用戶對象,參見“實現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.
查找與指定名稱相關聯的用戶對象,第14章“種植數據庫”時會用到這個方法
UpdateAsync(user)Pushes changes to a user object back into the database. See the “Implementing the Edit Feature” section.
將用戶對象的修改送入數據庫,參見“實現Edit特性”
UsersReturns an enumeration of the users. See the “Enumerating User Accounts” section.
返回用戶枚舉,參見“枚舉用戶賬號”

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.
提示:請注意方法名以Async結尾的那些方法。因為ASP.NET Identity幾乎完全是用C#的異步編程特性實現的,這意味著會并發地執行各種操作,而不會阻塞其他活動。在我開始演示如何創建和管理用戶數據時,你便會看到這種情況。對于每一個Async方法也有相應的同步擴展方法。對于大多數示例,我會堅持這種異步方法。但是,如果你需要按順序執行一些相關的操作,同步方法是有用的。在第14章的“種植數據庫”中就包含了一個這樣的例子。當你希望從屬性內部的getter或setter代碼塊中調用Identity方法時,同步方法也是有用的,在第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文件夾中添加了一個類文件,名稱為AppUserManager.cs,用它定義了用戶管理器類,名稱為AppUserManager,如清單13-7所示。

Listing 13-7. The Contents of the AppUserManager.cs File
清單13-7. AppUserManager.cs文件的內容

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需要一個AppUserManager的實例時,將會調用靜態的Create方法,這種情況將在對用戶數據執行操作時發生——也是在完成設置之后要演示的事情。

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:
為了創建AppUserManager類的實例,我需要一個UserStore<AppUser> 實例。這個UserStore<T> 類是IUserStore<T> 接口的Entity Framework實現,它提供了UserManager類所定義的存儲方法的實現。為了創建UserStore<AppUser> ,又需要AppIdentityDbContext類的一個實例,這是通過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.
被作為參數傳遞給Create方法的IOwinContext實現定義了一個泛型的Get方法,它會返回已經在OWIN啟動類中注冊的對象實例,啟動類在以下小節描述。

4. Creating the Start Class
4. 創建啟動類

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就緒并能運行,要做的最后一個片段是Start Class(啟動類)。在清單13-4中,我定義了一個應用程序設置,它為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是獨立出現在ASP.NET中的,并且有它自己的約定。其中之一就是,為了加載和配置中間件,并執行所需的其他配置工作,需要有一個被實例化的類。默認情況下,這個類叫做Start,而且是在全局命名空間中定義的。這個類含有一個名稱為Configuration的方法,該方法由OWIN基礎架構進行調用,并為該方法傳遞一個Owin.IAppBuilder接口的實現,由它支持應用程序所需中間件的設置。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.
我隨意地忽略了這一約定,因為我在MVC框架應用程序中使用的唯一OWIN中間件就是Identity。為了在應用程序的頂級命名空間定義一個類,我喜歡在Web.config文件中使用應用程序設置。于是我在App_Start文件夾中添加了一個類文件,名稱為IdentityConfig.cs,并用它定義了如清單13-8所示的類,這是我在Web.config文件中指定的一個類。

Listing 13-8. The Contents of the IdentityConfig.cs File
清單13-8. IdentityConfig.cs文件的內容

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接口是由一些擴展方法提供的,這些擴展方法的定義在Owin命名空間的一些類中。CreatePerOwinContext用于創建AppUserManager的新實例,AppIdentityDbContext類用于每一個請求。這樣保證每一個請求對ASP.NET Identity數據有清晰的訪問,我不必為同步時的情況或匱乏的數據緩存而操心。

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去標識已認證的用戶,以及通過CookieAuthenticationOptions類指定的選項在哪兒。這里重要的部分是LoginPath屬性,它指定了一個URL,這是未認證客戶端請求內容時要重定向的地址。我在其中指定了/Account/Login,將在第14章創建這個控制器來處理這些重定向。

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.
現在,已經完成了基本設置,可以開始使用ASP.NET Identity在示例應用程序中添加對用戶管理的支持了。在以下幾小節中,我將演示如何將Identity API用于創建管理工具,這樣能夠集中化地管理用戶。表13-4說明了ASP.NET Identity的情形。

Table 13-4. Putting Content Caching by Attribute in Context
表13-4. ASP.NET Identity的情形(這個標題作者寫錯了——譯者注)
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是用來管理用戶數據并執行認證和授權的API
Why should I care?
為什么要關注它?
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.
大多數應用程序都需要用戶創建賬號,并提供憑據去訪問內容和功能。ASP.NET Identity提供了執行這些操作的工具
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框架直接使用的,但它集成了標準的MVC授權特性

13.3.1 Enumerating User Accounts
13.3.1 枚舉用戶賬號

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.
集中化的用戶管理工具在幾乎所有應用程序中都是有用的,即使是那些允許用戶創建并管理自己賬號的情況也是如此。例如,總會有這樣一些客戶,他們需要大量創建賬號,并支持需要檢查和調整用戶數據的問題。根據本章的觀點,管理工具是有用的,因為它們將許多基本的用戶管理功能整合到了少量的幾個類之中,這對于演示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.
首先,在項目中添加一個控制器,名稱為Admin,如清單13-9所示,我將用它定義我的用戶管理功能。

Listing 13-9. The Contents of the AdminController.cs File
清單13-9. AdminController.cs文件的內容

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.
動作方法枚舉由Identity系統管理的用戶,當然,此刻還沒有任何用戶,但馬上就會有了。該清單重要的部分是我獲得類實例的方式,通過它,我可以管理用戶信息。在我實現不同的管理功能時,我會反復使用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類添加了一些擴展方法,其中之一便是GetOwinContext。它通過一個IOwinContext對象,將基于每請求的上下文對象提供給WOIN API。MVC框架應用程序對IOwinContext不感興趣,但是有另外一個擴展方法,叫做GetUserManager<T> ,可以用來得到用戶管理器類的實例。

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中有許多擴展方法,總體而言,這個API是一種混合體,它試圖將WOIN、抽象Identity功能以及具體的Entity Framework存儲實現混合在一起。

I called the GetUserManager with a generic type parameter to specify the AppUserManager class that I created earlier in the chapter, like this:
我用一個泛型參數調用了GetUserManager,用以指定本章前面創建的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類的實例,便可以開始查詢數據存儲了。AppUserManager.Users屬性返回了一個用戶對象的枚舉——應用程序中AppUser類的實例——于是可以用LINQ對這個枚舉進行查詢和維護。

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動作方法中,給View方法傳遞了Users屬性的值,以便能夠在視圖列出用戶的細節。清單13-10顯示了Views/Admin/Index.cshtml文件的內容,這是通過右擊Index動作方法,并從彈出菜單選擇“Add View(添加視圖)”而創建的。

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

@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.
這個視圖含有一個表格,每個用戶為一行,帶有唯一ID、用戶名以及E-mail地址的表格列。如果數據庫中沒有用戶,那么會顯示一條消息(圖中的“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.
在視圖中有一個Create的鏈接(其樣式用Bootstrap形成了一個按鈕),它的目標是Admin控制器中的Create動作。為了支持添加用戶,我會很快實現這個動作。

RESETTING THE DATABASE
重置數據庫

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.
當啟動應用程序并導航到/Admin/Index URL時,在視圖渲染的內容被顯示出來之前,會花一些時間。這是因為Entity Framework已經鏈接到數據庫,并發現此時尚未定義數據庫架構。Code First特性使用本章前面定義的類(以及包含在Identity程序集中的類)創建該架構,以便做好查詢和存儲數據的準備。

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對象資源管理器)”窗口,并展開IdentityDB數據庫架構,便可以看到其中包含了一些數據表,如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.
要刪除該數據庫,右擊IdentityDb條目,并從彈出菜單選擇“Delete(刪除)”。選中“Delete Database(刪除數據庫)”對話框中的復選框,并點擊OK,以刪除該數據庫。

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(數據庫)”條目,選擇“Add New Database(添加新數據庫)”(如圖13-3所示),并在“Database Name(數據庫名)”字段中輸入IdentityDb,點擊OK,便可以創建空數據庫。下一次啟動應用程序,并導航到Admin/Index URL時,Entity Framework又將偵測到沒有數據庫架構,又會重新創建此數據庫。

13.3.2 Creating Users
13.3.2 創建用戶

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.
對于應用程序接收的輸入,我打算使用MVC架構的模型驗證,最容易的做法是為控制器所支持的每一種操作創建一個簡單的視圖模型。我在Models文件夾中添加了一個類文件,名稱為serViewModels.cs,并用它定義了如清單13-11所示的類。隨著我為其他特性定義模型,會進一步地在這個文件中添加一些類。

Listing 13-11. The Contents of the UserViewModels.cs File
清單13-11. UserViewModels.cs文件的內容

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.
我所定義的第一個模型叫做CreateModel,它定義了創建用戶賬號所需要的基本屬性:用戶名、E-mail地址以及口令。這個模型中所定義的所有屬性都使用了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控制器中添加了一對Create動作方法,它們是上一小節Index視圖中的那個鏈接所指向的目標,而且,對于GET請求以及處理表單數據的POST請求,使用的是標準的控制器模式將視圖表現給用戶。可以從清單13-12中看到這兩個新的動作方法。

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

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為參數并在管理員遞交其表單數據時調用的那個Create動作方法(POST版的Create動作方法——譯者注)。我用ModelState.IsValid屬性檢查所接收到的數據是否包含了我所需要的數據,如果是,便創建一個AppUser類的新實例,并將它傳遞給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方法的結果是一個IdentityResult接口的實現,它通過表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
返回一個字符串枚舉,其中列出了嘗試操作期間所遇到的錯誤
SucceededReturns true if the operation succeeded
在操作成功時返回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.
你會注意到,維護用戶數據的所有操作,如清單13-12中所用到的UserManager.CreateAsync方法,都可以作為異步方法來使用。這種方法很容易與asyncawait關鍵字一起使用。使用異步的Identity方法讓你的動作方法能夠異步執行,這可以從整體上改善應用程序。

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提供的同步擴展方法。所有常用的異步方法都有一個同步的封裝程序,因此UserManager.CreateAsync方法的功能可以通過同步的UserManager.Create方法進行調用。我更喜歡使用異步方法,而且我建議在你的項目中遵循同樣的方式。當需要執行多個有依賴的操作時,為了形成簡單的代碼,同步方法可能是有用的,因此,作為一個演示,我在第14章的“種植數據庫”小節中使用了它們。

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動作方法中,我檢測了Succeeded屬性,以確定是否能夠在數據庫創建一條新的用戶記錄。如果Succeeded屬性為true,那么便將瀏覽器重定向到Index動作,以便顯示用戶列表:

...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,那么便調用AddErrorsFromResult方法,該方法枚舉了Errors屬性中的消息,并將它們添加到模型狀態的錯誤消息集合中,此過程利用了MVC框架的模型驗證特性。我定義AddErrorsFromResult方法,是因為隨著進一步地構建這個管理控制器的功能,還必須處理來自其他操作的錯誤消息。最后一步是創建視圖,讓管理員創建新賬號。清單13-13顯示了文件的Views/Admin/Create.cshtml內容。

Listing 13-13. The Contents of the Create.cshtml File
清單13-13. Create.cshtml文件的內容

@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.
該視圖沒有什么特別的地方——只是一個簡單的表單,用來收集一些值,MVC框架會將它們綁定到模型類的屬性上,然后傳遞給Create動作方法。

1. Testing the Create Functionality
1. 測試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.
為了測試創建新用戶賬號的能力,啟動應用程序,導航到/Admin/Index網址,并點擊Create按鈕。用一些值填充表單,如表13-6所示。

Table 13-6. The Values for Creating an Example User
表13-6. 創建示例用戶的值
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.
提示:雖然不是很多開發人員都知道,有一些域名是保留用于測試的,包括example.com。可以在https://tools.ietf.org/html/rfc2606網站看到完整的列表。

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.
一旦輸入了這些值,點擊“Create”按鈕。ASP.NET Identity將創建此用戶賬號,當瀏覽器被重定向到Index動作方法,會顯示出該用戶的信息,如圖13-5所示。從圖中可以看到不同的ID值,這是因為對于每個用戶賬號,ID是隨機生成的。

圖13-5

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

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.
再次點擊“Create”按鈕,并在表章中輸入同樣細節,即,使用表13-6中的值。當這一次遞交表單時,會看到一條通過模型驗證摘要報告的錯誤,如圖13-6所示。

圖13-6

Figure 13-6. An error trying to create a new user
圖13-6. 試圖創建新用戶時的錯誤

13.3.3 Validating Passwords
13.3.3 驗證口令

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.
一個最常用的需求,特別是對于公司的應用程序,是強制口令策略。ASP.NET Identity提供了一個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.
當設置為true時,合法口令必須含有非字母和數字的字符
RequireDigitWhen set to true, valid passwords must contain a digit.
當設置為true時,合法口令必須含有數字
RequireLowercaseWhen set to true, valid passwords must contain a lowercase character.
當設置為true時,合法口令必須含有小寫字母
RequireUppercaseWhen set to true, valid passwords must contain an uppercase character.
當設置為true時,合法口令必須含有大寫字母

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.
定義口令策略的辦法是,創建一個PasswordValidator類實例、設置其屬性的值,并在OWIN用來實例化AppUserManager類的Create方法中將該對象作為PasswordValidator屬性的值,如清單13-14所示。

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

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類指定了一個策略,它要求至少6個字符,并混用大小字符。通過啟動應用程序,便可以看到該口令策略的運用情況,導航到/Admin/Index網址,點擊Create按鈕,并嘗試創建一個有口令加密的賬號。若口令不滿足這一新策略,便會在模型狀態中添加一條錯誤消息,如圖13-7所示。

圖13-7

Figure 13-7. Reporting an error when validating a password
圖13-7. 驗證口令時報告的錯誤

Implementing a Custom Password Validator
實現自定義口令驗證器

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.
內建的口令驗證對于大多數應用程序足夠了,但你可能需要實現自定義的策略,尤其是在你實現一個公司的在線業務應用程序時,往往需要復雜的口令策略。這種對內建功能進行擴展的做法是,從PasswordValidatator派生一個新類,并重寫ValidateAsync方法。作為一個演示,我在Infrastructure文件夾中添加了一個類文件,名稱為CustomPasswordValidator.cs,如清單13-15所示。

Listing 13-15. The Contents of the CustomPasswordValidator.cs File
清單13-15. CustomPasswordValidator.cs文件的內容

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方法,并調用了基實現,因此能夠受益于內建的驗證檢查。給ValidateAsync方法傳遞了申請的口令,然后執行了自己的檢查,確保口令中不含數據序列12345。IdentityResult類的屬性是只讀的,這意味著,如果想報告驗證錯誤,則必須創建一個新實例,把這些錯誤與基實現的錯誤聯系在一起,并將這種組合而成的列表作為構造器參數進行傳遞。為了將基實現的錯誤與自定義錯誤聯系起來,我使用了LINQ。

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

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

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.
為了測試自定義口令驗證器,嘗試創建一個以口令密碼為12345的新用戶賬號。這會打破兩條規則——一條來自于內建驗證器,一條來自于自定義實現。兩個錯誤的錯誤消息被添加到了模型狀態,并在點擊Create按鈕時被顯示出來,如圖13-8所示。

圖13-8

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

13.3.4 Validating User Details
13.3.4 驗證用戶細節

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.
還可以執行更一般的驗證,辦法是創建UserValidator類的實例,并使用它所定義的屬性,以限制用戶其他屬性的值。表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.
當為true時,用戶名只能含有字母數字字符
RequireUniqueEmailWhen true, e-mail addresses must be unique.
當為true時,郵件地址必須唯一

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.
對用戶細節執行驗證的做法是創建UserValidator類實例,并在OWIN用來創建實例的Create方法中,將它賦給用戶管理器類的UserValidator屬性。清單13-17演示了使用內建驗證器類的一個示例。

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

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類有一個泛型的類型參數,它指定了用戶類的類型,即本示例中的AppUser。它的構造器參數是用戶管理器類,這是用戶管理器類(此應用程序中的AppUserManager)的一個實例。

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.
內建的驗證支持是相當基本的,但通過創建UserValidator的派生類,可以創建自定義驗證策略。作為一個演示,我在Infrastructure文件夾中添加了一個名稱為CustomUserValidator.cs的類文件,并用它創建了如清單13-19所示的類。

Listing 13-18. The Contents of the CustomUserValidator.cs File
清單13-18. CustomUserValidator.cs文件的內容

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.
這個派生類的構造器必須以用戶管理器類實例為參數,并調用基實現,才能夠執行內建的驗證檢查。自定義驗證是通過重寫ValidateAsync方法而實現的,該方法以用戶類實例為參數,并返回一個IdentityResult對象。上述自定義驗證策略將用戶的E-mail地址限制在example.com主域內,并執行了與口令驗證同樣的LINQ操作,將這里的錯誤消息與基類產生的錯誤消息關聯在一起。清單13-19演示了如何在AppUserManager類的Create方法中運用自定義驗證類,用以替換默認的實現。

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

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.
如果試圖創建一個E-mail地址為[email protected]的賬號,便會看到如圖13-9所示的結果。

圖13-9

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

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.
為了完成本例的管理工具,我只需實現編輯和刪除用戶的特性。在清單13-20中可以看到我對Views/Admin/Index.cshtml文件所做修改,它們的目標是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.
提示:你可能會注意到,我在Html.Begin輔助器的范圍內放入了一個Html.ActionLink調用,其目標為Edit動作方法。這么做純粹是為了讓Bootstrap將兩個元素的樣式作為按鈕顯示成一行。

13.4.1 Implementing the Delete Feature
13.4.1 實現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.
用戶管理器類定義了一個DeleteAsync方法,它以用戶類實例為參數,并將其從數據庫刪除。在清單13-21中,可以看到如何用DeleteAsync方法來實現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.
該動作方法以用戶的唯一ID作為參數,并且使用FindByIdAsync方法定位了相應的用戶對象,以便將該對象傳遞給DeleteAsync方法。DeleteAsync方法的結果是一個IdentityResult,對它的處理方式與之前示例的方式相同,以確保將任何錯誤都顯示給用戶。為了測試這個刪除功能,可以創建一個新的用戶,然后在Index視圖中點擊出現在該用戶旁邊的“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動作沒有相關聯的視圖,因此,為了將錯誤顯示出來,我在Views/Shared文件夾中創建了一個視圖文件,名稱為Error.cshtml,其內容如清單13-22所示。

Listing 13-22. The Contents of the Error.cshtml File
清單13-22. Error.cshtml文件的內容

@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章為了管理角色及其成員要創建的控制器。

13.4.2 Implementing the Edit Feature
13.4.2 實現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.
為了完成用戶管理工具,我需要為編輯用戶賬號的E-mail地址和口令的支持。此時這些是用戶定義僅有的屬性,不過到第15章時,將演示如何擴展數據庫架構,使用戶帶有自定義屬性。清單13-23顯示了添加到Admin控制器中的Edit動作方法。

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

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... // ...出于簡化,這里忽略了其他動作方法...
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請求作為目標的Edit動作使用Index視圖中的ID字符串調用FindByIdAsync方法,目的是獲得表示該用戶的AppUser對象。

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.
那個較復雜的實現接收POST請求,以用戶ID、新E-mail地址以及口令為參數。我必須執行幾個任務才能完成這種編輯操作。

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:
第一個任務是驗證接收到的值。此時使用的是一個簡單的用戶對象——盡管第15章將演示如何自定義存儲用戶的數據——但即使如此,還是需要驗證用戶數據,以確保不會與“驗證用戶細節”和“驗證口令”小節中定義的自定義策略相沖突。首先驗證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.
提示:注意,在執行驗證之前,必須修改Email屬性的值,因為ValidateAsync方法只接收用戶類的實例。

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).
下一個步驟是在已提供口令時,修改用戶口令。ASP.NET Identity存儲的是口令的哈希值,而不是口令本身——目的是防止口令被竊取。我的下一個步驟是取得這個已驗證的口令,并生成將被存儲到數據庫中的哈希碼,以便讓用戶能夠被認證,這將在第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:
將口令轉換成哈希值是通過IPasswordHasher接口的實現來做的,該接口實現是通過AppUserManager.PasswordHasher屬性獲得的。IPasswordHasher接口定義了HashPassword方法,它以一個字符串為參數,返回該字符串的哈希值,如下所示:

...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:
對用戶類的修改直到調用UpdateAsync方法時,才會存儲到數據庫中,如下所示:

...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. 創建視圖

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.
最后一個組件是渲染當前用戶值并將新值遞交給控制器的視圖。清單13-24顯示了Views/Admin/Edit.cshtml文件的內容。

Listing 13-24. The Contents of the Edit.cshtml File
清單13-24. Edit.cshtml文件的內容

@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顯示成靜態文本,這是不能修改的,并且提供了一個對E-mail地址和口令進行編輯的表單,如圖13-10所示。驗證問題會顯示在視圖的驗證摘要處,當成功編輯一個用戶賬號時,會返回到系統的賬號列表。

圖13-10

Figure 13-10. Editing a user account
圖13-10. 編輯用戶賬號

13.5 Summary
13.5 小結

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.
在本章中,我演示了如何創建使用ASP.NET Identity所需的配置和類,并且演示了如何運用它們來創建用戶管理工具。下一章將演示如何用ASP.NET Identity執行認證與授權。


看完此文如果覺得有所收獲,請給個推薦
你的推薦是我繼續下去的動力,也會讓更多人關注并獲益,這也是你的貢獻。


發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
主站蜘蛛池模板: 亚洲一区二区三区高清视频 | 亚洲精品午夜国产va久久成人 | 黄色三级网站 | 成年人视频免费 | 一级黄色国产视频 | 欧美自拍 | 欧美黑人xx | 国产资源在线免费观看 | 美女羞羞视频网站 | 91精品国产综合久久久欧美 | 撅高 自己扒开 调教 | 新久草在线视频 | 国产精品视频一区二区三区四区五区 | 亚洲国产高清自拍 | 免费黄色欧美视频 | 久久成人国产精品 | 成人午夜免费网站 | 国产99久久久久久免费看农村 | 黄色毛片视频在线观看 | 天天草夜夜爽 | 56av国产精品久久久久久久 | 日韩黄在线 | 他也色在线视频 | 成人一级黄色大片 | 99久久婷婷国产综合精品青牛牛 | 日韩理论电影网 | 国产高潮好爽受不了了夜色 | 久久综合综合久久 | 欧美成人一区二区三区 | www国产成人免费观看视频,深夜成人网 | 成人做爰s片免费看网站 | 黄色免费在线电影 | 欧美成人二区 | 国产成人精品区一区二区不卡 | 色综合激情| 伊人亚洲精品 | 国产成人高清在线观看 | 日日摸夜夜添夜夜添牛牛 | 中文字幕在线永久 | 欧美xxxx精品另类 | 草碰人人 |