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.cshtml和Views/Home/Index.cshtml文件。清单13-2显示了_Layout.cshtml文件的内容。
Listing 13-2. The Contents of the _Layout.cshtml File 清单13-2. _Layout.cshtml文件的内容
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所示的结果。
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所示。
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所示。
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(包管理器控制台)”中输入以下命令:
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文件
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所定义的内建属性(现在流行将“内建(Built-in)”说成“内置”,但我不会人云亦云,这两者在含义上是有区别的,“内建”完全是自己创建的,而“内置”有可能是别人做的东西拿来放入其中的——译者注),本章将使用这些属性。
Table 13-2. The Properties Defined by the IdentityUser Class 表13-2. IdentityUser类定义的属性
Name
名称
Description
描述
Claims
Returns the collection of claims for the user, which I describe in Chapter 15
返回用户的声明集合,关于声明(Claims)将在第15章描述
Email
Returns the user's e-mail address
返回用户的E-mail地址
Id
Returns the unique ID for the user
返回用户的唯一ID
Logins
Returns a collection of logins for the user, which I use in Chapter 15
返回用户的登录集合,将在第15章中使用
PasswordHash
Returns a hashed form of the user password, which I use in the “Implementing the Edit Feature” section
返回哈希格式的用户口令,在“实现Edit特性”中会用到它
Roles
Returns the collection of roles that the user belongs to, which I describe in Chapter 14
返回用户所属的角色集合,将在第14章描述
PhoneNumber
Returns the user's phone number
返回用户的电话号码
SecurityStamp
Returns a value that is changed when the user identity is altered, such as by a password change
返回变更用户标识时被修改的值,例如被口令修改的值
UserName
Returns 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特性”
Users
Returns 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指定配置类,如下所示:
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数据有清晰的访
请发表评论