65.9K
CodeProject 正在变化。 阅读更多。
Home

ASP.NET MVC5 使用自定义 UserStore 进行身份验证

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.88/5 (10投票s)

2014 年 12 月 22 日

CPOL

4分钟阅读

viewsIcon

57120

downloadIcon

2996

为 ASP.NET MVC5 身份验证实现自定义 UserStore

引言

Visual Studio 2013 为 ASP.NET MVC5 网站生成的代码能帮助我们快速入门,这是非常好的。由于模型-视图-控制器 (MVC) 模式的出色实现,现在构建一个外观精美且高度可维护的网站变得很容易。现在,让我们看看如何用自定义模型替换默认的基于 Entity Framework 的用户管理和身份验证模型。

背景

我花了几天时间尝试将我现有的基于 PHP Yii 的个人网站迁移到 ASP.NET。由于 Yii 框架是 MVC 框架,我也决定在 ASP.NET 中使用 MVC。言归正传,就身份验证而言,我的网站对它的要求很少。事实上,我之所以要深入研究 ASP.NET MVC5 身份验证系统,是因为一个简单的要求。我需要在站点的“管理”部分执行以下操作。

如果 (用户名 != "admin" 或密码 != "mypassword") 转到登录页面。仅此而已。我只需要一个硬编码的用户和密码来保护网站的一部分,不多也不少。

然而,由于 ASP.NET MVC 在身份验证/用户维护方面提供了固有的丰富功能,实现我这个简单的要求比我最初设想的需要我学习更多一些东西。

Using the Code

首先,我发现这方面的资料不多,所以我开始理解为了实现我的想法,哪些类和方法是真正需要的。下面是一张图,列出了我们需要使用的主要类。

Class Diagram

开箱即用的身份验证涉及 `ApplicationUser`、`ApplicationUserManager`、`ApplicationSignInManager` 和 `UserStore` 类。其模式是 `UserStore` 类负责存储/检索 `ApplicationUser` 的实例。`ApplicationUserManager` 和 `ApplicationSignInManager` 利用 `UserStore` 类来验证密码、检查账户锁定状态等。作为我们要求的一部分,我们将用非常基础的用户和用户存储类来替换 `ApplicationUser` 和 `UserStore` 类。

因此,我们从头开始实现的类是:

  • `SimpleUser` - 这将替换 IDE 生成的 `ApplicationUser` 类。
  • `XmlUserStore` - 这将替换生成的代码使用的内置 `UserStore` 类。

为了比检查用户 != admin || 密码 != 密码 稍微增加一些复杂性,`XmlUserStore` 将在 XML 文件中存储用户 ID 和密码。维护这个 XML 文件不是要求的一部分,`XmlUserStore` 只负责读取和传递用户 ID 和密码。如果我们想要添加其他功能,如添加新用户、修改、删除等,我们需要实现这些方法。我们会遇到其中一些,但目前它们只抛出 `NotImplementedException`。

重要的是,我们要实现正确的接口,并且只为我们感兴趣的方法提供完整的实现。

  • `SimpleUser` 实现基本的 `IUser` 接口,并有一个 `GenerateUserIdentityAsync` 方法。
  • `XmlUserStore` 类实现 `IUserStore`、`IUserPasswordStore` 和 `IUserLockoutStore` 接口。
  • `XmlUserStore` 的重要方法是:
    • `IUserStore` 的 `FindByIdAsync(string userId)`
    • `IUserStore` 的 `FindByNameAsync(string userName)`
    • `IUserPasswordStore` 的 `GetPasswordHashAsync(SimpleUser user)`
    • `IUserLockoutStore` 的 `GetLockoutEnabledAsync(SimpleUser user)`

这些是我们实现目标所需的一切。一旦这两个类到位,我们就需要修改 IDE 生成的代码,使其使用这些类而不是内置的 `User` 和 `UserStore` 类。`App_Start`/IdentityConfig.cs 文件是我们开始修改的地方。`ApplicationUserManager` 是身份验证工作的核心。我们首先更改其派生:

public class ApplicationUserManager : UserManager<SimpleUser>

为了理解起见,以下方法已在 `ApplicationUserManager` 中全新实现,用于记录调用,但最终会调用基类的实现。

public override Task<SimpleUser> FindAsync(string userName, string password)
public override Task<SimpleUser> FindByEmailAsync(string email)
public override Task<SimpleUser> FindByNameAsync(string userName)
protected override Task<bool> VerifyPasswordAsync(IUserPasswordStore<SimpleUser, string> store, 
    SimpleUser user, string password)
public override Task<bool> IsLockedOutAsync(string userId)
public override Task<bool> GetTwoFactorEnabledAsync(string userId)
public override Task<ClaimsIdentity> CreateIdentityAsync(SimpleUser user, string authenticationType)

`static` 方法 `Create()` 创建 `ApplicationUserManager` 类,并初始化 `XmlUserStore`,后者需要在构造函数中指定 `credentials-xml-file`。密码使用 ASP.NET 的 `PasswordHasher` 类进行离线哈希处理并存储在文件中。文件内容如下:

<?xml version="1.0" encoding="utf-8" ?>
<users>
    <user id=pop@pop.com 
    password="ACWUFWmyVcWeLugKyLOrZJzDUpqmmUzeDZ+w0/Dbs2YAy7Xj5FfmdS65GRXX8lLTDw==" />
</users>

要测试 `WebApplication` 示例,请使用登录名:pop@pop.com 和密码:pop。

下一个需要修改的重要类是 `ApplicationSignInManager`。我们更改其派生:

public class ApplicationSignInManager : SignInManager<SimpleUser, string>

为了理解起见,以下方法已在 `ApplicationSignInManager` 中全新实现,用于记录调用,但最终会调用基类的实现。

public override Task<ClaimsIdentity> CreateUserIdentityAsync(SimpleUser user)
public override Task<SignInStatus> PasswordSignInAsync
    (string userName, string password, bool isPersistent, bool shouldLockout)
public static ApplicationSignInManager Create
    (IdentityFactoryOptions<ApplicationSignInManager> options, IOwinContext context)

请注意,我已从 *IdentityConfig.cs* 文件中删除了 `EmailService` 和 `SmsService` 类,因为它们与此讨论无关。

我们列表中的下一个文件是 *App_Start/Startup.Auth.cs*。我们修改 `public void ConfigureAuth(IAppBuilder app)` 方法并在此处使用 `SimpleUser` 类:

OnValidateIdentity = SecurityStampValidator.OnValidateIdentity<ApplicationUserManager, SimpleUser>(
                        validateInterval: TimeSpan.FromMinutes(30),
                        regenerateIdentity: (manager, user) => 
            user.GenerateUserIdentityAsync(manager))<applicationusermanager, simpleuser="">

由于我们不支持用户添加、编辑和其他相关管理任务,因此所有相关方法都已从 *AccountController.cs* 文件中删除。唯一重要的方法是 `Login()` 和 `LogOff()`。

现在我们已经有了使用 ASP.NET MVC 身份验证框架工作的自定义身份验证。

关注点

并非我们绝对必须实现 `UserStore` 类,因为 `ApplicationUserManager` 方法只是将调用委托给 `UserStore` 类中的相应方法,但从关注点分离的角度来看,坚持 MVC 开发者为我们设计的模式可能是更好的选择。

© . All rights reserved.