zoukankan      html  css  js  c++  java
  • 定制与扩展Asp.NET 5 MVC内建身份验证机制

    背景

    在需要进行表单认证的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
    View Code

    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

      
      
      

    Step 6. 在类库项目中创建Models

      6.1 创建Models文件夹
        该文件夹用于保存用户验证相关的模型类,这些类都继承自Microsoft.AspNet.Identity.EntityFramework命名空间下相应的类,并显示指定了关键字的类型为long(Asp.NET默认使用string类型)
      6.2 创建MyLogin类
    namespace Core.Models
    {
        public class MyLogin : IdentityUserLogin<long>
        {
        }
    }
    View Code
      6.3 创建MyUserRole类
    namespace Core.Models
    {
        public class MyUserRole : IdentityUserRole<long>
        {
        }
    }
    View Code
      6.4 创建MyClaim类
    namespace Core.Models
    {
        public class MyClaim : IdentityUserClaim<long>
        {
        }
    }
    View Code
      6.5 创建MyRole类
    namespace Core.Models
    {
        public class MyRole : IdentityRole<long, MyUserRole>
        {
        }
    }
    View Code
      6.6 创建MyUser类
    namespace Core.Models
    {
        public class MyUser : IdentityUser<long, MyLogin, MyUserRole, MyClaim>
        {
    
            #region properties
    
            public string ActivationToken { get; set; }
    
            public string PasswordAnswer { get; set; }
    
            public string PasswordQuestion { get; set; }
    
            #endregion
    
            #region methods
    
            public async Task<ClaimsIdentity> GenerateUserIdentityAsync(MyUserManager userManager)
            {
                var userIdentity = await userManager.CreateIdentityAsync(this, DefaultAuthenticationTypes.ApplicationCookie);
                // Add custom user claims here
                return userIdentity;
            }
    
            #endregion
    
        }
    }
    View Code

    Step 7. 创建MyUserManager类

    namespace Core.Models
    {
        public class MyUserManager : UserManager<MyUser, long>
        {
    
            #region constructors and destructors
    
            public MyUserManager(IUserStore<MyUser, long> store)
                : base(store)
            {
            }
    
            #endregion
    
            #region methods
    
            public static MyUserManager Create(IdentityFactoryOptions<MyUserManager> options, IOwinContext context)
            {
                var manager = new MyUserManager(new UserStore<MyUser, MyRole, long, MyLogin, MyUserRole, MyClaim>(context.Get<ApplicationDbContext>()));
                // Configure validation logic for usernames
                manager.UserValidator = new UserValidator<MyUser, long>(manager)
                {
                    AllowOnlyAlphanumericUserNames = false,
                    RequireUniqueEmail = true
                };
                // Configure validation logic for passwords
                manager.PasswordValidator = new PasswordValidator
                {
                    RequiredLength = 6,
                    RequireNonLetterOrDigit = true,
                    RequireDigit = true,
                    RequireLowercase = true,
                    RequireUppercase = true,
                };
                // Register two factor authentication providers. This application uses Phone and Emails as a step of receiving a code for verifying the user
                // You can write your own provider and plug in here.
                manager.RegisterTwoFactorProvider(
                    "PhoneCode",
                    new PhoneNumberTokenProvider<MyUser, long>
                    {
                        MessageFormat = "Your security code is: {0}"
                    });
                manager.RegisterTwoFactorProvider(
                    "EmailCode",
                    new EmailTokenProvider<MyUser, long>
                    {
                        Subject = "Security Code",
                        BodyFormat = "Your security code is: {0}"
                    });
                manager.EmailService = new MyIdentityEmailService();
                manager.SmsService = new MyIdentitySmsService(); ;
                var dataProtectionProvider = options.DataProtectionProvider;
                if (dataProtectionProvider != null)
                {
                    manager.UserTokenProvider = new DataProtectorTokenProvider<MyUser, long>(dataProtectionProvider.Create("ASP.NET Identity"));
                }
                return manager;
            }
    
            #endregion
    
        }
    }
    View Code

    Step 8. 创建MyIdentityEmailService.cs和MyIdentitySmsService.cs

    namespace Core
    {
        public class MyIdentityEmailService : IIdentityMessageService
        {
            #region methods
    
            public Task SendAsync(IdentityMessage message)
            {
                // Plug in your email service here to send an email.
                return Task.FromResult(0);
            }
    
            #endregion
        }
    }
    View Code
    namespace Core.Models
    {
        public class MyIdentitySmsService : IIdentityMessageService
        {
            public Task SendAsync(IdentityMessage message)
            {
                // Plug in your sms service here to send a text message.
                return Task.FromResult(0);
            }
        }
    }
    View Code

      Microsoft.AspNet.Identity提供了IIdentityMessageService接口,MyIdentityEmailService和MyIdentitySmsService都继承了IIdentityMessageService接口,用于向用户发送Email和短信通知

    Step 9. 创建ApplicationDbContext.cs

      Asp.NET Identity使用Entityframework作为用户数据库的ORM,ApplicationDbContext继承了Microsoft.AspNet.Identity.EntityFramework.IdentityDbContext,并将我们刚刚创建的那些类指定为DbContext的操作对象
    namespace Core
    {
        public class ApplicationDbContext : IdentityDbContext<MyUser, MyRole, long, MyLogin, MyUserRole, MyClaim>
        {
    
            #region constructors and destructors
    
            public ApplicationDbContext()
                : base("DefaultConnection")
            {
            }
    
            #endregion
    
            #region methods
    
            public static ApplicationDbContext Create()
            {
                return new ApplicationDbContext();
            }
    
            protected override void OnModelCreating(DbModelBuilder modelBuilder)
            {
                base.OnModelCreating(modelBuilder);
                // Map Entities to their tables.
                modelBuilder.Entity<MyUser>().ToTable("User");
                modelBuilder.Entity<MyRole>().ToTable("Role");
                modelBuilder.Entity<MyClaim>().ToTable("UserClaim");
                modelBuilder.Entity<MyLogin>().ToTable("UserLogin");
                modelBuilder.Entity<MyUserRole>().ToTable("UserRole");
                // Set AutoIncrement-Properties
                modelBuilder.Entity<MyUser>().Property(r => r.Id).HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
                modelBuilder.Entity<MyClaim>().Property(r => r.Id).HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
                modelBuilder.Entity<MyRole>().Property(r => r.Id).HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
                // Override some column mappings that do not match our default
                modelBuilder.Entity<MyUser>().Property(r => r.UserName).HasColumnName("Login");
                modelBuilder.Entity<MyUser>().Property(r => r.PasswordHash).HasColumnName("Password");
            }
    
            #endregion
    
        }
    }
    View Code

    Step 9. 在MVC项目中添加对Core项目的引用

    Step 10. 通过Buget移除并重新添加Microsoft ASP.NET Identity Owin和Microsoft ASP.NET Identity Framework包

      因为在Core项目中引用到的这两个包的版本高于Asp.NET MVC默认提供的版本,因此需要重新添加对它们的引用,保持版本一致性
     

    Step 11. 修改默认Asp.net MVC项目中与用户验证相关的ViewModel,View和Controller,使其使用我们自建的模型、UserNamager与DbContext。首先从ViewModel开始,打开MVC项目下Models文件夹中的AccountViewModels.cs,修改后的文件如下所示

    using System.ComponentModel.DataAnnotations;
    
    namespace MyMvcProject.Models
    {
        public class ExternalLoginConfirmationViewModel
        {
            [Required]
            [EmailAddress]
            [Display(Name = "Email")]
            public string Email { get; set; }
        }
    
        public class ExternalLoginListViewModel
        {
            public string Action { get; set; }
            public string ReturnUrl { get; set; }
        }
    
        public class ManageUserViewModel
        {
            [Required]
            [DataType(DataType.Password)]
            [Display(Name = "Current password")]
            public string OldPassword { get; set; }
    
            [Required]
            [StringLength(100, ErrorMessage = "The {0} must be at least {2} characters long.", MinimumLength = 6)]
            [DataType(DataType.Password)]
            [Display(Name = "New password")]
            public string NewPassword { get; set; }
    
            [DataType(DataType.Password)]
            [Display(Name = "Confirm new password")]
            [Compare("NewPassword", ErrorMessage = "The new password and confirmation password do not match.")]
            public string ConfirmPassword { get; set; }
        }
    
        public class LoginViewModel
        {
            [Required]
            [EmailAddress]
            [Display(Name = "Email")]
            public string Email { get; set; }
    
            [Required]
            [DataType(DataType.Password)]
            [Display(Name = "Password")]
            public string Password { get; set; }
    
            [Display(Name = "Remember me?")]
            public bool RememberMe { get; set; }
        }
    
        public class RegisterViewModel
        {
            [Required]
            [EmailAddress]
            [Display(Name = "Email")]
            public string Email { get; set; }
    
            [Required]
            [StringLength(100, ErrorMessage = "The {0} must be at least {2} characters long.", MinimumLength = 6)]
            [DataType(DataType.Password)]
            [Display(Name = "Password")]
            public string Password { get; set; }
    
            [DataType(DataType.Password)]
            [Display(Name = "Confirm password")]
            [Compare("Password", ErrorMessage = "The password and confirmation password do not match.")]
            public string ConfirmPassword { get; set; }
        }
    
        public class ForgotPasswordViewModel
        {
            [Required]
            [EmailAddress]
            [Display(Name = "Email")]
            public string Email { get; set; }
        }
    
        public class ResetPasswordViewModel
        {
            [Required]
            [EmailAddress]
            [Display(Name = "Email")]
            public string Email { get; set; }
    
            [Required]
            [StringLength(100, ErrorMessage = "The {0} must be at least {2} characters long.", MinimumLength = 6)]
            [DataType(DataType.Password)]
            [Display(Name = "Password")]
            public string Password { get; set; }
    
            [DataType(DataType.Password)]
            [Display(Name = "Confirm password")]
            [Compare("Password", ErrorMessage = "The password and confirmation password do not match.")]
            public string ConfirmPassword { get; set; }
    
            public string Code { get; set; }
        }
    
    }
    View Code

    Step 12. 接下来是Controller, 打开MVC项目下Controllers文件夹中的AccountController.cs,修改后的文件如下所示

    using System;
    using System.Threading.Tasks;
    using System.Web;
    using System.Web.Mvc;
    using Core.Models;
    using Microsoft.AspNet.Identity;
    using Microsoft.AspNet.Identity.Owin;
    using Microsoft.Owin.Security;
    using MyMvcProject.Models;
    
    namespace MyMvcProject.Controllers
    {
    
        [Authorize]
        public class AccountController : Controller
        {
    
            #region constants
    
            private const string XsrfKey = "XsrfId";
    
            #endregion
    
            #region member vars
    
            private MyUserManager _userManager;
    
            #endregion
    
            #region enums
    
            public enum ManageMessageId
            {
                ChangePasswordSuccess,
                SetPasswordSuccess,
                RemoveLoginSuccess,
                Error
            }
    
            #endregion
    
            #region properties
    
            public MyUserManager UserManager
            {
                get
                {
                    return _userManager ?? HttpContext.GetOwinContext().GetUserManager<MyUserManager>();
                }
                private set
                {
                    _userManager = value;
                }
            }
    
            private IAuthenticationManager AuthenticationManager
            {
                get
                {
                    return HttpContext.GetOwinContext().Authentication;
                }
            }
    
            #endregion
    
            #region constructors and destructors
    
            public AccountController()
            {
            }
    
            public AccountController(MyUserManager userManager)
            {
                UserManager = userManager;
            }
    
            protected override void Dispose(bool disposing)
            {
                if (disposing && UserManager != null)
                {
                    UserManager.Dispose();
                    UserManager = null;
                }
                base.Dispose(disposing);
            }
    
            #endregion
    
            #region methods
    
            [AllowAnonymous]
            public async Task<ActionResult> ConfirmEmail(long userId, string code)
            {
                if (userId == null || code == null)
                {
                    return View("Error");
                }
    
                var result = await UserManager.ConfirmEmailAsync(userId, code);
                if (result.Succeeded)
                {
                    return View("ConfirmEmail");
                }
                AddErrors(result);
                return View();
            }
    
            //
            // POST: /Account/Disassociate
            [HttpPost]
            [ValidateAntiForgeryToken]
            public async Task<ActionResult> Disassociate(string loginProvider, string providerKey)
            {
                ManageMessageId? message = null;
                var result = await UserManager.RemoveLoginAsync(long.Parse(User.Identity.GetUserId()), new UserLoginInfo(loginProvider, providerKey));
                if (result.Succeeded)
                {
                    var user = await UserManager.FindByIdAsync(long.Parse(User.Identity.GetUserId()));
                    await SignInAsync(user, false);
                    message = ManageMessageId.RemoveLoginSuccess;
                }
                else
                {
                    message = ManageMessageId.Error;
                }
                return RedirectToAction(
                    "Manage",
                    new
                    {
                        Message = message
                    });
            }
    
            //
            // POST: /Account/ExternalLogin
            [HttpPost]
            [AllowAnonymous]
            [ValidateAntiForgeryToken]
            public ActionResult ExternalLogin(string provider, string returnUrl)
            {
                // Request a redirect to the external login provider
                return new ChallengeResult(
                    provider,
                    Url.Action(
                        "ExternalLoginCallback",
                        "Account",
                        new
                        {
                            ReturnUrl = returnUrl
                        }));
            }
    
            //
            // GET: /Account/ExternalLoginCallback
            [AllowAnonymous]
            public async Task<ActionResult> ExternalLoginCallback(string returnUrl)
            {
                var loginInfo = await AuthenticationManager.GetExternalLoginInfoAsync();
                if (loginInfo == null)
                {
                    return RedirectToAction("Login");
                }
    
                // Sign in the user with this external login provider if the user already has a login
                var user = await UserManager.FindAsync(loginInfo.Login);
                if (user != null)
                {
                    await SignInAsync(user, false);
                    return RedirectToLocal(returnUrl);
                }
                // If the user does not have an account, then prompt the user to create an account
                ViewBag.ReturnUrl = returnUrl;
                ViewBag.LoginProvider = loginInfo.Login.LoginProvider;
                return View(
                    "ExternalLoginConfirmation",
                    new ExternalLoginConfirmationViewModel
                    {
                        Email = loginInfo.Email
                    });
            }
    
            //
            // POST: /Account/ExternalLoginConfirmation
            [HttpPost]
            [AllowAnonymous]
            [ValidateAntiForgeryToken]
            public async Task<ActionResult> ExternalLoginConfirmation(ExternalLoginConfirmationViewModel model, string returnUrl)
            {
                if (User.Identity.IsAuthenticated)
                {
                    return RedirectToAction("Manage");
                }
    
                if (ModelState.IsValid)
                {
                    // Get the information about the user from the external login provider
                    var info = await AuthenticationManager.GetExternalLoginInfoAsync();
                    if (info == null)
                    {
                        return View("ExternalLoginFailure");
                    }
                    var user = new MyUser
                    {
                        UserName = model.Email,
                        Email = model.Email
                    };
                    var result = await UserManager.CreateAsync(user);
                    if (result.Succeeded)
                    {
                        result = await UserManager.AddLoginAsync(user.Id, info.Login);
                        if (result.Succeeded)
                        {
                            await SignInAsync(user, false);
    
                            // For more information on how to enable account confirmation and password reset please visit http://go.microsoft.com/fwlink/?LinkID=320771
                            // Send an email with this link
                            // string code = await UserManager.GenerateEmailConfirmationTokenAsync(user.Id);
                            // var callbackUrl = Url.Action("ConfirmEmail", "Account", new { userId = user.Id, code = code }, protocol: Request.Url.Scheme);
                            // SendEmail(user.Email, callbackUrl, "Confirm your account", "Please confirm your account by clicking this link");
    
                            return RedirectToLocal(returnUrl);
                        }
                    }
                    AddErrors(result);
                }
    
                ViewBag.ReturnUrl = returnUrl;
                return View(model);
            }
    
            //
            // GET: /Account/ExternalLoginFailure
            [AllowAnonymous]
            public ActionResult ExternalLoginFailure()
            {
                return View();
            }
    
            [AllowAnonymous]
            public ActionResult ForgotPassword()
            {
                return View();
            }
    
            //
            // POST: /Account/ForgotPassword
            [HttpPost]
            [AllowAnonymous]
            [ValidateAntiForgeryToken]
            public async Task<ActionResult> ForgotPassword(ForgotPasswordViewModel model)
            {
                if (ModelState.IsValid)
                {
                    var user = await UserManager.FindByNameAsync(model.Email);
                    if (user == null || !(await UserManager.IsEmailConfirmedAsync(user.Id)))
                    {
                        ModelState.AddModelError("", "The user either does not exist or is not confirmed.");
                        return View();
                    }
    
                    // For more information on how to enable account confirmation and password reset please visit http://go.microsoft.com/fwlink/?LinkID=320771
                    // Send an email with this link
                    // string code = await UserManager.GeneratePasswordResetTokenAsync(user.Id);
                    // var callbackUrl = Url.Action("ResetPassword", "Account", new { userId = user.Id, code = code }, protocol: Request.Url.Scheme);        
                    // await UserManager.SendEmailAsync(user.Id, "Reset Password", "Please reset your password by clicking <a href="" + callbackUrl + "">here</a>");
                    // return RedirectToAction("ForgotPasswordConfirmation", "Account");
                }
    
                // If we got this far, something failed, redisplay form
                return View(model);
            }
    
            //
            // GET: /Account/ForgotPasswordConfirmation
            [AllowAnonymous]
            public ActionResult ForgotPasswordConfirmation()
            {
                return View();
            }
    
            //
            // POST: /Account/LinkLogin
            [HttpPost]
            [ValidateAntiForgeryToken]
            public ActionResult LinkLogin(string provider)
            {
                // Request a redirect to the external login provider to link a login for the current user
                return new ChallengeResult(provider, Url.Action("LinkLoginCallback", "Account"), User.Identity.GetUserId());
            }
    
            //
            // GET: /Account/LinkLoginCallback
            public async Task<ActionResult> LinkLoginCallback()
            {
                var loginInfo = await AuthenticationManager.GetExternalLoginInfoAsync(XsrfKey, User.Identity.GetUserId());
                if (loginInfo == null)
                {
                    return RedirectToAction(
                        "Manage",
                        new
                        {
                            Message = ManageMessageId.Error
                        });
                }
                var result = await UserManager.AddLoginAsync(long.Parse(User.Identity.GetUserId()), loginInfo.Login);
                if (result.Succeeded)
                {
                    return RedirectToAction("Manage");
                }
                return RedirectToAction(
                    "Manage",
                    new
                    {
                        Message = ManageMessageId.Error
                    });
            }
    
            //
            // POST: /Account/LogOff
            [HttpPost]
            [ValidateAntiForgeryToken]
            public ActionResult LogOff()
            {
                AuthenticationManager.SignOut();
                return RedirectToAction("Index", "Home");
            }
    
            //
            // GET: /Account/Login
            [AllowAnonymous]
            public ActionResult Login(string returnUrl)
            {
                ViewBag.ReturnUrl = returnUrl;
                return View();
            }
    
            //
            // POST: /Account/Login
            [HttpPost]
            [AllowAnonymous]
            [ValidateAntiForgeryToken]
            public async Task<ActionResult> Login(LoginViewModel model, string returnUrl)
            {
                if (ModelState.IsValid)
                {
                    var user = await UserManager.FindAsync(model.Email, model.Password);
                    if (user != null)
                    {
                        await SignInAsync(user, model.RememberMe);
                        return RedirectToLocal(returnUrl);
                    }
                    ModelState.AddModelError("", "Invalid username or password.");
                }
    
                // If we got this far, something failed, redisplay form
                return View(model);
            }
    
            //
            // GET: /Account/Manage
            public ActionResult Manage(ManageMessageId? message)
            {
                ViewBag.StatusMessage =
                    message == ManageMessageId.ChangePasswordSuccess ? "Your password has been changed."
                    : message == ManageMessageId.SetPasswordSuccess ? "Your password has been set."
                    : message == ManageMessageId.RemoveLoginSuccess ? "The external login was removed."
                    : message == ManageMessageId.Error ? "An error has occurred."
                    : "";
                ViewBag.HasLocalPassword = HasPassword();
                ViewBag.ReturnUrl = Url.Action("Manage");
                return View();
            }
    
            //
            // POST: /Account/Manage
            [HttpPost]
            [ValidateAntiForgeryToken]
            public async Task<ActionResult> Manage(ManageUserViewModel model)
            {
                var hasPassword = HasPassword();
                ViewBag.HasLocalPassword = hasPassword;
                ViewBag.ReturnUrl = Url.Action("Manage");
                if (hasPassword)
                {
                    if (ModelState.IsValid)
                    {
                        var result = await UserManager.ChangePasswordAsync(long.Parse(User.Identity.GetUserId()), model.OldPassword, model.NewPassword);
                        if (result.Succeeded)
                        {
                            var user = await UserManager.FindByIdAsync(long.Parse(User.Identity.GetUserId()));
                            await SignInAsync(user, false);
                            return RedirectToAction(
                                "Manage",
                                new
                                {
                                    Message = ManageMessageId.ChangePasswordSuccess
                                });
                        }
                        AddErrors(result);
                    }
                }
                else
                {
                    // User does not have a password so remove any validation errors caused by a missing OldPassword field
                    var state = ModelState["OldPassword"];
                    if (state != null)
                    {
                        state.Errors.Clear();
                    }
    
                    if (ModelState.IsValid)
                    {
                        var result = await UserManager.AddPasswordAsync(long.Parse(User.Identity.GetUserId()), model.NewPassword);
                        if (result.Succeeded)
                        {
                            return RedirectToAction(
                                "Manage",
                                new
                                {
                                    Message = ManageMessageId.SetPasswordSuccess
                                });
                        }
                        AddErrors(result);
                    }
                }
    
                // If we got this far, something failed, redisplay form
                return View(model);
            }
    
            //
            // GET: /Account/Register
            [AllowAnonymous]
            public ActionResult Register()
            {
                return View();
            }
    
            //
            // POST: /Account/Register
            [HttpPost]
            [AllowAnonymous]
            [ValidateAntiForgeryToken]
            public async Task<ActionResult> Register(RegisterViewModel model)
            {
                if (ModelState.IsValid)
                {
                    var user = new MyUser
                    {
                        UserName = model.Email,
                        Email = model.Email
                    };
                    try
                    {
                        var result = await UserManager.CreateAsync(user, model.Password);
                        if (result.Succeeded)
                        {
                            await SignInAsync(user, false);
    
                            // For more information on how to enable account confirmation and password reset please visit http://go.microsoft.com/fwlink/?LinkID=320771
                            // Send an email with this link
                            // string code = await UserManager.GenerateEmailConfirmationTokenAsync(user.Id);
                            // var callbackUrl = Url.Action("ConfirmEmail", "Account", new { userId = user.Id, code = code }, protocol: Request.Url.Scheme);
                            // await UserManager.SendEmailAsync(user.Id, "Confirm your account", "Please confirm your account by clicking <a href="" + callbackUrl + "">here</a>");
    
                            return RedirectToAction("Index", "Home");
                        }
                        AddErrors(result);
                    }
                    catch (Exception ex)
                    {
                        throw (ex);
                    }
                }
    
                // If we got this far, something failed, redisplay form
                return View(model);
            }
    
            [ChildActionOnly]
            public ActionResult RemoveAccountList()
            {
                var linkedAccounts = UserManager.GetLogins(long.Parse(User.Identity.GetUserId()));
                ViewBag.ShowRemoveButton = HasPassword() || linkedAccounts.Count > 1;
                return PartialView("_RemoveAccountPartial", linkedAccounts);
            }
    
            [AllowAnonymous]
            public ActionResult ResetPassword(string code)
            {
                if (code == null)
                {
                    return View("Error");
                }
                return View();
            }
    
            //
            // POST: /Account/ResetPassword
            [HttpPost]
            [AllowAnonymous]
            [ValidateAntiForgeryToken]
            public async Task<ActionResult> ResetPassword(ResetPasswordViewModel model)
            {
                if (ModelState.IsValid)
                {
                    var user = await UserManager.FindByNameAsync(model.Email);
                    if (user == null)
                    {
                        ModelState.AddModelError("", "No user found.");
                        return View();
                    }
                    var result = await UserManager.ResetPasswordAsync(user.Id, model.Code, model.Password);
                    if (result.Succeeded)
                    {
                        return RedirectToAction("ResetPasswordConfirmation", "Account");
                    }
                    AddErrors(result);
                    return View();
                }
    
                // If we got this far, something failed, redisplay form
                return View(model);
            }
    
            //
            // GET: /Account/ResetPasswordConfirmation
            [AllowAnonymous]
            public ActionResult ResetPasswordConfirmation()
            {
                return View();
            }
    
            #endregion
    
            #region Helpers
    
            private async Task SignInAsync(MyUser user, bool isPersistent)
            {
                AuthenticationManager.SignOut(DefaultAuthenticationTypes.ExternalCookie);
                var identity = await UserManager.CreateIdentityAsync(user, DefaultAuthenticationTypes.ApplicationCookie);
                AuthenticationManager.SignIn(new AuthenticationProperties() { IsPersistent = isPersistent }, identity);
            }
    
            private void AddErrors(IdentityResult result)
            {
                foreach (var error in result.Errors)
                {
                    ModelState.AddModelError("", error);
                }
            }
    
            private bool HasPassword()
            {
                var user = UserManager.FindById(long.Parse(User.Identity.GetUserId()));
                if (user != null)
                {
                    return user.PasswordHash != null;
                }
                return false;
            }
    
            private ActionResult RedirectToLocal(string returnUrl)
            {
                if (Url.IsLocalUrl(returnUrl))
                {
                    return Redirect(returnUrl);
                }
                else
                {
                    return RedirectToAction("Index", "Home");
                }
            }
    
            #endregion
    
            private class ChallengeResult : HttpUnauthorizedResult
            {
                #region constructors and destructors
    
                public ChallengeResult(string provider, string redirectUri)
                    : this(provider, redirectUri, null)
                {
                }
    
                public ChallengeResult(string provider, string redirectUri, string userId)
                {
                    LoginProvider = provider;
                    RedirectUri = redirectUri;
                    UserId = userId;
                }
    
                #endregion
    
                #region properties
    
                public string LoginProvider { get; set; }
    
                public string RedirectUri { get; set; }
    
                public string UserId { get; set; }
    
                #endregion
    
                #region methods
    
                public override void ExecuteResult(ControllerContext context)
                {
                    var properties = new AuthenticationProperties
                    {
                        RedirectUri = RedirectUri
                    };
                    if (UserId != null)
                    {
                        properties.Dictionary[XsrfKey] = UserId;
                    }
                    context.HttpContext.GetOwinContext().Authentication.Challenge(properties, LoginProvider);
                }
    
                #endregion
            }
        }
    }
    View Code

    Step 13. 最后是Views,涉及的文件稍多,但都位于ViewsAccount目录下

       _ChangePasswordPartial.cshtml
    @using Microsoft.AspNet.Identity
    @model MyMvcProject.Models.ManageUserViewModel
    
    <p>You're logged in as <strong>@User.Identity.GetUserName()</strong>.</p>
    
    @using (Html.BeginForm("Manage", "Account", FormMethod.Post, new { @class = "form-horizontal", role = "form" }))
    {
        @Html.AntiForgeryToken()
        <h4>Change Password Form</h4>
        <hr />
        @Html.ValidationSummary()
        <div class="form-group">
            @Html.LabelFor(m => m.OldPassword, new { @class = "col-md-2 control-label" })
            <div class="col-md-10">
                @Html.PasswordFor(m => m.OldPassword, new { @class = "form-control" })
            </div>
        </div>
        <div class="form-group">
            @Html.LabelFor(m => m.NewPassword, new { @class = "col-md-2 control-label" })
            <div class="col-md-10">
                @Html.PasswordFor(m => m.NewPassword, new { @class = "form-control" })
            </div>
        </div>
        <div class="form-group">
            @Html.LabelFor(m => m.ConfirmPassword, new { @class = "col-md-2 control-label" })
            <div class="col-md-10">
                @Html.PasswordFor(m => m.ConfirmPassword, new { @class = "form-control" })
            </div>
        </div>
    
        <div class="form-group">
            <div class="col-md-offset-2 col-md-10">
                <input type="submit" value="Change password" class="btn btn-default" />
            </div>
        </div>
    }
    View Code
      _ExternalLoginsListPartial.cshtml
    @using Microsoft.Owin.Security
    
    <h4>Use another service to log in.</h4>
    <hr />
    @{
        var loginProviders = Context.GetOwinContext().Authentication.GetExternalAuthenticationTypes();
        if (loginProviders.Count() == 0)
        {
            <div>
                <p>
                    There are no external authentication services configured. See <a href="http://go.microsoft.com/fwlink/?LinkId=313242">this article</a>
                    for details on setting up this ASP.NET application to support logging in via external services.
                </p>
            </div>
        }
        else
        {
            string action = Model.Action;
            string returnUrl = Model.ReturnUrl;
            using (Html.BeginForm(action, "Account", new { ReturnUrl = returnUrl }))
            {
                @Html.AntiForgeryToken()
                <div id="socialLoginList">
                    <p>
                        @foreach (AuthenticationDescription p in loginProviders)
                        {
                            <button type="submit" class="btn btn-default" id="@p.AuthenticationType" name="provider" value="@p.AuthenticationType" title="Log in using your @p.Caption account">@p.AuthenticationType</button>
                        }
                    </p>
                </div>
            }
        }
    }
    View Code
      _RemoveAccountPartial.cshtml
    @model ICollection<Microsoft.AspNet.Identity.UserLoginInfo>
    
    @if (Model.Count > 0)
    {
        <h4>Registered Logins</h4>
        <table class="table">
            <tbody>
                @foreach (var account in Model)
                {
                    <tr>
                        <td>@account.LoginProvider</td>
                        <td>
                            @if (ViewBag.ShowRemoveButton)
                            {
                                using (Html.BeginForm("Disassociate", "Account"))
                                {
                                    @Html.AntiForgeryToken()
                                    <div>
                                        @Html.Hidden("loginProvider", account.LoginProvider)
                                        @Html.Hidden("providerKey", account.ProviderKey)
                                        <input type="submit" class="btn btn-default" value="Remove" title="Remove this @account.LoginProvider login from your account" />
                                    </div>
                                }
                            }
                            else
                            {
                                @: &nbsp;
                            }
                        </td>
                    </tr>
                }
            </tbody>
        </table>
    }
    View Code
       _SetPasswordPartial.cshtml
    @model MyMvcProject.Models.ManageUserViewModel
    
    <p class="text-info">
        You do not have a local username/password for this site. Add a local
        account so you can log in without an external login.
    </p>
    
    @using (Html.BeginForm("Manage", "Account", FormMethod.Post, new { @class = "form-horizontal", role = "form" }))
    {
        @Html.AntiForgeryToken()
    
        <h4>Create Local Login</h4>
        <hr />
        @Html.ValidationSummary()
        <div class="form-group">
            @Html.LabelFor(m => m.NewPassword, new { @class = "col-md-2 control-label" })
            <div class="col-md-10">
                @Html.PasswordFor(m => m.NewPassword, new { @class = "form-control" })
            </div>
        </div>
        <div class="form-group">
            @Html.LabelFor(m => m.ConfirmPassword, new { @class = "col-md-2 control-label" })
            <div class="col-md-10">
                @Html.PasswordFor(m => m.ConfirmPassword, new { @class = "form-control" })
            </div>
        </div>
        <div class="form-group">
            <div class="col-md-offset-2 col-md-10">
                <input type="submit" value="Set password" class="btn btn-default" />
            </div>
        </div>
    }
    View Code
      ConfirmEmail.cshtml
    @{
        ViewBag.Title = "ConfirmAccount";
    }
    
    <h2>@ViewBag.Title.</h2>
    <div>
        <p>
            Thank you for confirming your account. Please @Html.ActionLink("click here to log in", "Login", "Account", routeValues: null, htmlAttributes: new { id = "loginLink" })
        </p>
    </div>
    View Code
      ExternalLoginConfirmation.cshtml
    @model MyMvcProject.Models.ExternalLoginConfirmationViewModel
    @{
        ViewBag.Title = "Register";
    }
    <h2>@ViewBag.Title.</h2>
    <h3>Associate your @ViewBag.LoginProvider account.</h3>
    
    @using (Html.BeginForm("ExternalLoginConfirmation", "Account", new { ReturnUrl = ViewBag.ReturnUrl }, FormMethod.Post, new { @class = "form-horizontal", role = "form" }))
    {
        @Html.AntiForgeryToken()
    
        <h4>Association Form</h4>
        <hr />
        @Html.ValidationSummary(true, "", new { @class = "text-danger" })
        <p class="text-info">
            You've successfully authenticated with <strong>@ViewBag.LoginProvider</strong>.
            Please enter a user name for this site below and click the Register button to finish
            logging in.
        </p>
        <div class="form-group">
            @Html.LabelFor(m => m.Email, new { @class = "col-md-2 control-label" })
            <div class="col-md-10">
                @Html.TextBoxFor(m => m.Email, new { @class = "form-control" })
                @Html.ValidationMessageFor(m => m.Email, "", new { @class = "text-danger" })
            </div>
        </div>
        <div class="form-group">
            <div class="col-md-offset-2 col-md-10">
                <input type="submit" class="btn btn-default" value="Register" />
            </div>
        </div>
    }
    
    @section Scripts {
        @Scripts.Render("~/bundles/jqueryval")
    }
    View Code
      ExternalLoginFailure.cshtml
    @{
        ViewBag.Title = "Login Failure";
    }
    
    <h2>@ViewBag.Title.</h2>
    <h3 class="text-error">Unsuccessful login with service.</h3>
    View Code
      ForgotPassword.cshtml
    @model MyMvcProject.Models.ForgotPasswordViewModel
    @{
        ViewBag.Title = "Forgot your password?";
    }
    
    <h2>@ViewBag.Title.</h2>
    
    @using (Html.BeginForm("ForgotPassword", "Account", FormMethod.Post, new { @class = "form-horizontal", role = "form" }))
    {
        @Html.AntiForgeryToken()
        <h4>Enter your email.</h4>
        <hr />
        @Html.ValidationSummary("", new { @class = "text-danger" })
        <div class="form-group">
            @Html.LabelFor(m => m.Email, new { @class = "col-md-2 control-label" })
            <div class="col-md-10">
                @Html.TextBoxFor(m => m.Email, new { @class = "form-control" })
            </div>
        </div>
        <div class="form-group">
            <div class="col-md-offset-2 col-md-10">
                <input type="submit" class="btn btn-default" value="Email Link" />
            </div>
        </div>
    }
    
    @section Scripts {
        @Scripts.Render("~/bundles/jqueryval")
    }
    View Code
      ForgotPasswordConfirmation.cshtml
    @{
        ViewBag.Title = "Forgot Password Confirmation";
    }
    
    <hgroup class="title">
        <h1>@ViewBag.Title.</h1>
    </hgroup>
    <div>
        <p>
            Please check your email to reset your password.
        </p>
    </div>
    View Code
      Login.cshtml
    @using MyMvcProject.Models
    @model LoginViewModel
    
    @{
        ViewBag.Title = "Log in";
    }
    
    <h2>@ViewBag.Title.</h2>
    <div class="row">
        <div class="col-md-8">
            <section id="loginForm">
                @using (Html.BeginForm("Login", "Account", new { ReturnUrl = ViewBag.ReturnUrl }, FormMethod.Post, new { @class = "form-horizontal", role = "form" }))
                {
                    @Html.AntiForgeryToken()
                    <h4>Use a local account to log in.</h4>
                    <hr />
                    @Html.ValidationSummary(true, "", new { @class = "text-danger" })
                    <div class="form-group">
                        @Html.LabelFor(m => m.Email, new { @class = "col-md-2 control-label" })
                        <div class="col-md-10">
                            @Html.TextBoxFor(m => m.Email, new { @class = "form-control" })
                            @Html.ValidationMessageFor(m => m.Email, "", new { @class = "text-danger" })
                        </div>
                    </div>
                    <div class="form-group">
                        @Html.LabelFor(m => m.Password, new { @class = "col-md-2 control-label" })
                        <div class="col-md-10">
                            @Html.PasswordFor(m => m.Password, new { @class = "form-control" })
                            @Html.ValidationMessageFor(m => m.Password, "", new { @class = "text-danger" })
                        </div>
                    </div>
                    <div class="form-group">
                        <div class="col-md-offset-2 col-md-10">
                            <div class="checkbox">
                                @Html.CheckBoxFor(m => m.RememberMe)
                                @Html.LabelFor(m => m.RememberMe)
                            </div>
                        </div>
                    </div>
                    <div class="form-group">
                        <div class="col-md-offset-2 col-md-10">
                            <input type="submit" value="Log in" class="btn btn-default" />
                        </div>
                    </div>
                    <p>
                        @Html.ActionLink("Register as a new user", "Register")
                    </p>
                    @* Enable this once you have account confirmation enabled for password reset functionality
                        <p>
                            @Html.ActionLink("Forgot your password?", "ForgotPassword")
                        </p>*@
                }
            </section>
        </div>
        <div class="col-md-4">
            <section id="socialLoginForm">
                @Html.Partial("_ExternalLoginsListPartial", new ExternalLoginListViewModel { Action = "ExternalLogin", ReturnUrl = ViewBag.ReturnUrl })
            </section>
        </div>
    </div>
    @section Scripts {
        @Scripts.Render("~/bundles/jqueryval")
    }
    View Code
      Manage.cshtml
    @{
        ViewBag.Title = "Manage Account";
    }
    
    <h2>@ViewBag.Title.</h2>
    
    <p class="text-success">@ViewBag.StatusMessage</p>
    <div class="row">
        <div class="col-md-12">
            @if (ViewBag.HasLocalPassword)
            {
                @Html.Partial("_ChangePasswordPartial")
            }
            else
            {
                @Html.Partial("_SetPasswordPartial")
            }
    
            <section id="externalLogins">
                @Html.Action("RemoveAccountList")
                @Html.Partial("_ExternalLoginsListPartial", new { Action = "LinkLogin", ReturnUrl = ViewBag.ReturnUrl })
            </section>
        </div>
    </div>
    @section Scripts {
        @Scripts.Render("~/bundles/jqueryval")
    }
    View Code
      Register.cshtml
    @model MyMvcProject.Models.RegisterViewModel
    @{
        ViewBag.Title = "Register";
    }
    
    <h2>@ViewBag.Title.</h2>
    
    @using (Html.BeginForm("Register", "Account", FormMethod.Post, new { @class = "form-horizontal", role = "form" }))
    {
        @Html.AntiForgeryToken()
        <h4>Create a new account.</h4>
        <hr />
        @Html.ValidationSummary("", new { @class = "text-danger" })
        <div class="form-group">
            @Html.LabelFor(m => m.Email, new { @class = "col-md-2 control-label" })
            <div class="col-md-10">
                @Html.TextBoxFor(m => m.Email, new { @class = "form-control" })
            </div>
        </div>
        <div class="form-group">
            @Html.LabelFor(m => m.Password, new { @class = "col-md-2 control-label" })
            <div class="col-md-10">
                @Html.PasswordFor(m => m.Password, new { @class = "form-control" })
            </div>
        </div>
        <div class="form-group">
            @Html.LabelFor(m => m.ConfirmPassword, new { @class = "col-md-2 control-label" })
            <div class="col-md-10">
                @Html.PasswordFor(m => m.ConfirmPassword, new { @class = "form-control" })
            </div>
        </div>
        <div class="form-group">
            <div class="col-md-offset-2 col-md-10">
                <input type="submit" class="btn btn-default" value="Register" />
            </div>
        </div>
    }
    
    @section Scripts {
        @Scripts.Render("~/bundles/jqueryval")
    }
    View Code
      ResetPassword.cshtml
    @model MyMvcProject.Models.ResetPasswordViewModel
    @{
        ViewBag.Title = "Reset password";
    }
    
    <h2>@ViewBag.Title.</h2>
    
    @using (Html.BeginForm("ResetPassword", "Account", FormMethod.Post, new { @class = "form-horizontal", role = "form" }))
    {
        @Html.AntiForgeryToken()
        <h4>Reset your password.</h4>
        <hr />
        @Html.ValidationSummary("", new { @class = "text-danger" })
        @Html.HiddenFor(model => model.Code)
        <div class="form-group">
            @Html.LabelFor(m => m.Email, new { @class = "col-md-2 control-label" })
            <div class="col-md-10">
                @Html.TextBoxFor(m => m.Email, new { @class = "form-control" })
            </div>
        </div>
        <div class="form-group">
            @Html.LabelFor(m => m.Password, new { @class = "col-md-2 control-label" })
            <div class="col-md-10">
                @Html.PasswordFor(m => m.Password, new { @class = "form-control" })
            </div>
        </div>
        <div class="form-group">
            @Html.LabelFor(m => m.ConfirmPassword, new { @class = "col-md-2 control-label" })
            <div class="col-md-10">
                @Html.PasswordFor(m => m.ConfirmPassword, new { @class = "form-control" })
            </div>
        </div>
        <div class="form-group">
            <div class="col-md-offset-2 col-md-10">
                <input type="submit" class="btn btn-default" value="Reset" />
            </div>
        </div>
    }
    
    @section Scripts {
        @Scripts.Render("~/bundles/jqueryval")
    }
    View Code
      ResetPasswordConfirmation.cshtml
    @{
        ViewBag.Title = "Reset password confirmation";
    }
    
    <hgroup class="title">
        <h1>@ViewBag.Title.</h1>
    </hgroup>
    <div>
        <p>
            Your password has been reset. Please @Html.ActionLink("click here to log in", "Login", "Account", routeValues: null, htmlAttributes: new { id = "loginLink" })
        </p>
    </div>
    View Code

    Step 14. 以下两行必须添加到App_StartStartup.Auth.cs中,以便在MVC程序初始化时创建ApplicationDbContext和MyUserManager的单例

      app.CreatePerOwinContext(ApplicationDbContext.Create);
      app.CreatePerOwinContext<MyUserManager>(MyUserManager.Create);

      修改后的Startup.Auth.cs文件如下:
    namespace MyMvcProject
    {
        public partial class Startup
        {
            // For more information on configuring authentication, please visit http://go.microsoft.com/fwlink/?LinkId=301864
            public void ConfigureAuth(IAppBuilder app)
            {
                // Configure the db context and user manager to use a single instance per request
                app.CreatePerOwinContext(ApplicationDbContext.Create);
                app.CreatePerOwinContext<MyUserManager>(MyUserManager.Create);
    
                //other codes
                ......    
            }
        }
    }

    Step 15. 试运行MVC项目,网站正常运行后,先注册用户:

      
      注册成功
      

    Step 16. 检查目标数据库,用户数据已保存至Web.config文件所指定的目标数据库User表中

      

    Step 17. 至此,针对自备数据库的基于表单认证的Asp.net Identity定制的工作已经完成

     

     
    以上项目与数据库脚本可以从我的Github下载:https://github.com/heuyang/IdentityCustomizationExample/archive/master.zip
     
    我的QQ: 384059,欢迎和我一样在做企业信息系统的同道中人加我的QQ互相交流,一起成长
     
  • 相关阅读:
    Python之路系列:面向对象初级:静态属性、静态方法、类方法
    对象和类
    Python的函数参数传递
    python参数传递:对象的引用
    Python的locals()函数
    Python 异常处理
    Python变量类型的强制转换
    日常问题总结
    高效能人士的七个习惯
    Dojo入门:DOM操作
  • 原文地址:https://www.cnblogs.com/heuyang/p/5062388.html
Copyright © 2011-2022 走看看