冯 海

一个程序新人菜鸟的日记,希望大家多多关照。QQ:32316131

【ASP.NET Identity系列教程(1)】ASP.NET Identity入门

13.2.4 Creating the Entity Framework Classes
13.2.4 创建Entity Framework类

1. 创建用户类

第一个要定义的类是表现一个用户的类,我将它称为“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
返回用户名

 

提示:Microsoft.AspNet.Identity.EntityFramework命名空间中的类是, Microsoft.AspNet.Identity命名空间中所定义接口的Entity Framework专用的具体实现。例如,IdentityUser便是IUser接口的实现。我会使用这些具体类,因为我要依靠Entity Framework在数据库中存储我的用户数据,等到ASP.NET Identity变得成熟时,你可能会期望看到这些接口的其他实现,它们使用了不同的存储机制(当然,大多数项目仍然会使用Entity Framework,因为它来自于微软)。

目前最重要的是这个IdentityUser类只提供了对用户基本信息的访问:用户名、E-mail、电话、哈希口令、角色成员等等。如果希望存储用户的各种附加信息,就需要在IdentityUser派生的类上添加属性,并将它用于表示应用程序中的用户,第15章中将演示其做法。

为了创建应用程序中的用户类,我在Models文件夹中创建了一个类文件,名称为AppUser.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
        // 这里将放置附加属性
    }
}

  以上便是此刻要做的全部工作,第15章会再次讨论这个类,那时会演示如何添加应用程序专用的用户数据属性。

2. 创建数据库上下文类

下一个步骤是创建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
            // 初始化配置将放在这儿
        }
    }
}

  这个AppIdentityDbContext类的构造器调用了它的基类,其参数是连接字符串的名称,IdentityDb,用于与数据库连接。这是让清单13-4定义的连接字符串与ASP.NET Identity联结起来的方法。

这个AppIdentityDbContext类还定义了一个静态的构造器,它使用Database.SetInitializer方法指定了一个种植数据库的类(种植数据库的含义是往数据库中植入数据的意思,说穿了,就是用一些数据对数据库进行初始化——译者注),当通过Entity Framework的Code First特性第一次创建数据库架构时,会用到这个类。这个种植类叫做IdentityDbInit,而且我已经提供了一个类,创建了一个占位符,后面可以回过头来在PerformInitialSetup方法中添加语句,就可以种植数据库了。第14章将演示如何种植数据库。

最后,AppIdentityDbContext类定义了一个Create方法。这是由OWIN在必要时创建类实例的办法,这个由OWIN使用的类将在“创建启动类”中进行描述。

注:如果对这些类的意义无法理解,不用担心。如果不熟悉Entity Framework,我建议你将它视为是某种黑箱事物。一旦基础构建块就绪——而且对本章的这些代码进行拷贝——那么就几乎不需要编辑它们了。

3. Creating the User Manager Class
3. 创建用户管理器类

最重要的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.
返回用户枚举,参见“枚举用户账号”


提示:请注意方法名以Async结尾的那些方法。因为ASP.NET Identity几乎完全是用C#的异步编程特性实现的,这意味着会并发地执行各种操作,而不会阻塞其他活动。在我开始演示如何创建和管理用户数据时,你便会看到这种情况。对于每一个Async方法也有相应的同步扩展方法。对于大多数示例,我会坚持这种异步方法。但是,如果你需要按顺序执行一些相关的操作,同步方法是有用的。在第14章的“种植数据库”中就包含了一个这样的例子。当你希望从属性内部的getter或setter代码块中调用Identity方法时,同步方法也是有用的,在第15章中,我就是这么做的。

我在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;
        }
    }
}

  在Identity需要一个AppUserManager的实例时,将会调用静态的Create方法,这种情况将在对用户数据执行操作时发生——也是在完成设置之后要演示的事情。

为了创建AppUserManager类的实例,我需要一个UserStore<AppUser> 实例。这个UserStore<T> 类是IUserStore<T> 接口的Entity Framework实现,它提供了UserManager类所定义的存储方法的实现。为了创建UserStore<AppUser> ,又需要AppIdentityDbContext类的一个实例,这是通过OWIN按如下办法得到的:

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

  被作为参数传递给Create方法的IOwinContext实现定义了一个泛型的Get方法,它会返回已经在OWIN启动类中注册的对象实例,启动类在以下小节描述。

4. 创建启动类

为了使ASP.NET Identity就绪并能运行,要做的最后一个片段是Start Class(启动类)。在清单13-4中,我定义了一个应用程序设置,它为OWIN指定配置类,如下所示

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

  

OWIN是独立出现在ASP.NET中的,并且有它自己的约定。其中之一就是,为了加载和配置中间件,并执行所需的其他配置工作,需要有一个被实例化的类。默认情况下,这个类叫做Start,而且是在全局命名空间中定义的。这个类含有一个名称为Configuration的方法,该方法由OWIN基础架构进行调用,并为该方法传递一个Owin.IAppBuilder接口的实现,由它支持应用程序所需中间件的设置。Start类通常被定义成分部类,还有一些其他的类文件,它们分别专用于要用的每一种中间件。 

是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"),
            });
        }
    }
}

  IAppBuilder接口是由一些扩展方法提供的,这些扩展方法的定义在Owin命名空间的一些类中。CreatePerOwinContext用于创建AppUserManager的新实例,AppIdentityDbContext类用于每一个请求。这样保证每一个请求对ASP.NET Identity数据有清晰的访问,我不必为同步时的情况或匮乏的数据缓存而操心。

 

UseCookieAuthentication方法告诉ASP.NET Identity如何用Cookie去标识已认证的用户,以及通过CookieAuthenticationOptions类指定的选项在哪儿。这里重要的部分是LoginPath属性,它指定了一个URL,这是未认证客户端请求内容时要重定向的地址。我在其中指定了/Account/Login,将在第14章创建这个控制器来处理这些重定向。

13.3 使用ASP.NET Identity

现在,已经完成了基本设置,可以开始使用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 枚举用户账号

集中化的用户管理工具在几乎所有应用程序中都是有用的,即使是那些允许用户创建并管理自己账号的情况也是如此。例如,总会有这样一些客户,他们需要大量创建账号,并支持需要检查和调整用户数据的问题。根据本章的观点,管理工具是有用的,因为它们将许多基本的用户管理功能整合到了少量的几个类之中,这对于演示ASP.NET Identity的基本特性是很有用的。

首先,在项目中添加一个控制器,名称为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>();
            }
        }
    }
}

  

动作方法枚举由Identity系统管理的用户,当然,此刻还没有任何用户,但马上就会有了。该清单重要的部分是我获得类实例的方式,通过它,我可以管理用户信息。在我实现不同的管理功能时,我会反复使用AppUserManager类,而且,我在Admin控制器中定义了UserManager属性,以便简化代码。

 
Microsoft.Owin.Host.SystemWeb程序集为HttpContext类添加了一些扩展方法,其中之一便是GetOwinContext。它通过一个IOwinContext对象,将基于每请求的上下文对象提供给WOIN API。MVC框架应用程序对IOwinContext不感兴趣,但是有另外一个扩展方法,叫做GetUserManager<T> ,可以用来得到用户管理器类的实例。

Microsoft.Owin.Host.SystemWeb程序集为HttpContext类添加了一些扩展方法,其中之一便是GetOwinContext。它通过一个IOwinContext对象,将基于每请求的上下文对象提供给WOIN API。MVC框架应用程序对IOwinContext不感兴趣,但是有另外一个扩展方法,叫做GetUserManager<T> ,可以用来得到用户管理器类的实例。

提示:正如你所推断的一样,ASP.NET Identity中有许多扩展方法,总体而言,这个API是一种混合体,它试图将WOIN、抽象Identity功能以及具体的Entity Framework存储实现混合在一起。

我用一个泛型参数调用了GetUserManager,用以指定本章前面创建的AppUserManager类,如下所示:

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

  一旦有了AppUserManager类的实例,便可以开始查询数据存储了。AppUserManager.Users属性返回了一个用户对象的枚举——应用程序中AppUser类的实例——于是可以用LINQ对这个枚举进行查询和维护。

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" })

  

这个视图含有一个表格,每个用户为一行,带有唯一ID、用户名以及E-mail地址的表格列。如果数据库中没有用户,那么会显示一条消息(图中的“No User Accounts”消息——译者注),如图13-4所示。

图13-4
 
 

 

 

 

 

 

posted @ 2017-05-09 19:52  秋天来了哟  阅读(357)  评论(0)    收藏  举报
认识就是缘份,愿天下人都快乐!
QQ: 32316131
Email: 32316131@qq.com