背景
在需要进行表单认证的Asp.NET 5 MVC项目被创建后,往往需要根据项目的实际需求做一系列的工作对MVC 5内建的身份验证机制(Asp.NET Identity)进行扩展和定制:
- Asp.NET内建的身份验证机制会使用Local DB(本地数据库)读写用户相关的信息,而在数据库驱动的项目中,管理业务信息的数据库通常是特定的数据库环境,比如远程SQL Server数据库实例或Access数据库等等,业务数据库中保存着一系列针对业务需求的数据表,因此需要定制MVC 5内建身份验证,使其操作的的用户表们与业务数据库的表们共处在同一数据库中
- Asp.NET身份验证默认创建的用户表名为:AspNetRoles, AspNetUserClaims, AspNetUserLogins, AspNetUserRoles, AspNetUsers等,与实际业务数据库中自成体系的数据表命名习惯(如tblProduct, PRODUCT, Products...)不一致,因此需要定制MVC 5内建身份验证,使其使用我们指定的表名称保存用户信息,以便与实际业务数据库中的表名称处于相同的命名规范体系
- 实际业务中用户信息往往多于Asp.NET默认提供的,如根据实际情况会需要以用户email登录,或在Users表中保存用户的guid,性别,地址,是否激活等等,因此需要对Asp.net创建的表,以及相应操作的代码进行扩展
总之,一切都是为了减轻管理的负担,提升工作效率,使项目整体变得更加优雅。
要点
本文仅聚焦在表单身份认证(Forms Authentication)的个性化定制
步骤
Step 1. 创建SQL Server数据库,并运行以下SQL,创建示例用户数据表
CREATE TABLE [dbo].[User] ( [Id] [bigint] IDENTITY(1,1) NOT NULL, [Login] [nvarchar](50) NOT NULL, [EMail] [nvarchar](255) NOT NULL, [Password] [nvarchar](500) NULL, [CreationDate] [datetime] NULL, [ApprovalDate] [datetime] NULL, [LastLoginDate] [datetime] NULL, [IsLocked] [bit] NOT NULL, [PasswordQuestion] [nvarchar](max) NULL, [PasswordAnswer] [nvarchar](max) NULL, [ActivationToken] [nvarchar](200) NULL, [EmailConfirmed] [bit] NOT NULL, [SecurityStamp] [nvarchar](max) NULL, [PhoneNumber] [nvarchar](50) NULL, [PhoneNumberConfirmed] [bit] NOT NULL, [TwoFactorEnabled] [bit] NOT NULL, [LockoutEndDateUtc] [datetime2](7) NULL, [LockoutEnabled] [bit] NOT NULL, [AccessFailedCount] [int] NOT NULL, CONSTRAINT [PK_User] PRIMARY KEY CLUSTERED ( [Id] ASC ), CONSTRAINT [UX_User_EMail] UNIQUE NONCLUSTERED ( [EMail] ASC ), CONSTRAINT [UX_User_Login] UNIQUE NONCLUSTERED ( [Login] ASC ) ) GO ALTER TABLE [dbo].[User] ADD CONSTRAINT [DF_User_IsLocked] DEFAULT ((0)) FOR [IsLocked] GO ALTER TABLE [dbo].[User] ADD CONSTRAINT [DF_User_EmailConfirmed] DEFAULT ((0)) FOR [EmailConfirmed] GO ALTER TABLE [dbo].[User] ADD CONSTRAINT [DF_User_PhoneNumberConfirmed] DEFAULT ((0)) FOR [PhoneNumberConfirmed] GO ALTER TABLE [dbo].[User] ADD CONSTRAINT [DF_User_TwoFactorEnabled] DEFAULT ((0)) FOR [TwoFactorEnabled] GO ALTER TABLE [dbo].[User] ADD CONSTRAINT [DF_User_LockoutEnabled] DEFAULT ((0)) FOR [LockoutEnabled] GO ALTER TABLE [dbo].[User] ADD CONSTRAINT [DF_User_AccessFailCount] DEFAULT ((0)) FOR [AccessFailedCount] GO CREATE TABLE [UserRegistrationToken] ( [Id] [bigint] IDENTITY(1,1) NOT NULL, [UserId] [bigint] NULL, [Token] [nchar](10) NOT NULL, CONSTRAINT [PK_SecurityToken] PRIMARY KEY CLUSTERED ( [Id] ASC ), CONSTRAINT [UX_UserRegistrationToken_Token] UNIQUE NONCLUSTERED ( [Token] ASC ) ) GO CREATE TABLE [dbo].[Role] ( [Id] BIGINT IDENTITY (1, 1) NOT NULL, [Name] NVARCHAR (MAX) NOT NULL, CONSTRAINT [PK_Role] PRIMARY KEY CLUSTERED ([Id] ASC) ) GO CREATE TABLE [dbo].[UserRole] ( [UserId] BIGINT NOT NULL, [RoleId] BIGINT NOT NULL, CONSTRAINT [PK_UserRole] PRIMARY KEY CLUSTERED ([UserId] ASC, [RoleId] ASC), CONSTRAINT [FK_UserRole_Role] FOREIGN KEY ([RoleId]) REFERENCES [dbo].[Role] ([Id]) ON DELETE CASCADE, CONSTRAINT [FK_UserRole_User] FOREIGN KEY ([UserId]) REFERENCES [dbo].[User] ([Id]) ON DELETE CASCADE ) GO CREATE NONCLUSTERED INDEX [IX_RoleId] ON [dbo].[UserRole]([RoleId] ASC); GO CREATE NONCLUSTERED INDEX [IX_UserId] ON [dbo].[UserRole]([UserId] ASC); GO CREATE TABLE [dbo].[UserLogin] ( [UserId] BIGINT NOT NULL, [LoginProvider] NVARCHAR (128) NOT NULL, [ProviderKey] NVARCHAR (128) NOT NULL, CONSTRAINT [PK_UserLogin] PRIMARY KEY CLUSTERED ([UserId] ASC, [LoginProvider] ASC, [ProviderKey] ASC), CONSTRAINT [FK_UserLogin_User] FOREIGN KEY ([UserId]) REFERENCES [dbo].[User] ([Id]) ON DELETE CASCADE ) GO CREATE NONCLUSTERED INDEX [IX_UserId] ON [dbo].[UserLogin]([UserId] ASC); GO CREATE TABLE [dbo].[UserClaim] ( [Id] BIGINT IDENTITY (1, 1) NOT NULL, [UserId] BIGINT NOT NULL, [ClaimType] NVARCHAR (MAX) NULL, [ClaimValue] NVARCHAR (MAX) NULL, CONSTRAINT [PK_UserClaim] PRIMARY KEY CLUSTERED ([Id] ASC), CONSTRAINT [FK_UserClaim_User] FOREIGN KEY ([UserId]) REFERENCES [dbo].[User] ([Id]) ON DELETE CASCADE ) GO CREATE NONCLUSTERED INDEX [IX_User_Id] ON [dbo].[UserClaim]([UserId] ASC); GO
Step 2. 创建MVC示例项目
运行Visual Studio 2013 -> 新建项目 -> Visual C# -> Web -> ASP.NET Web Application,输入MVC项目的名称,确定
在接下来的项目设置界面中,选择MVC项目,认证方式选择"个别用户帐户"
Step 3. 创建单独的类库,用于保存业务模型,数据库关系映射,业务逻辑等
实际项目中,我个人很喜欢把业务模型,数据库关系映射,业务逻辑等根据实际情况放到独立的类库项目中。即使很小型的简单项目,也会至少把与前端表示层不相关的代码归拢到一个类库里面,便于管理
解决方案浏览器中右击解决方案节点 -> "添加..." -> 新项目
新建项目窗口中,选择Visual C# -> Windows -> 类库 -> 输入项目名称 (本例中用Core命名) -> 确定 -> 删除自动创建的Class1.cs
Step 4. 更新MVC项目中的数据库连接字符串
因为我们的目标是使用自己的数据库而非Asp.NET默认的,因此需要首先修改MVC项目中的连接字符串
打开Web.config,找到<connectionStrings>节点,对名为DefaultConnection的connectionString进行修改:
<add name="DefaultConnection" connectionString="Server=myserver;Database=mydatabase;User Id=myuserid;Password=mypassword;" providerName="System.Data.SqlClient" />
Step 5. 在类库项目中引用所需的Nuget包
Microsoft ASP.NET Identity Owin和Microsoft ASP.NET Identity Framework,本项目中引用的这两个包的版本为2.2.1