作者:Steve Smith
ASP.NET Core标识是一种可扩展系统,可用于创建自定义存储提供程序并将其连接到你的应用。 本主题介绍如何创建 ASP.NET Core标识的自定义的存储提供程序。 它介绍了用于创建自己的存储提供程序的重要概念,但并不是分步演练。
默认情况下,ASP.NET Core标识系统中使用实体框架核心的 SQL Server 数据库存储用户信息。 对于许多应用程序而言,这种方法的效果很好。 但是,你可能希望使用不同的持久性机制或数据架构。 例如:
在上述每种情况下,都可以为存储机制编写自定义的提供程序,并将该提供程序插入到应用程序中。
ASP.NET Core 标识包含在 Visual Studio 中使用"单个用户帐户"选项的项目模板中。
在使用.NET Core CLI 时,添加-au Individual
:
dotnet new mvc -au Individual
ASP.NET Core标识包含类称为管理器和存储区。 管理器是应用程序开发人员用来执行操作的高级类,如创建标识用户。 存储是用于指定如何保存实体(如用户和角色)的较低级别类。 存储遵循存储库模式,并与持久性机制紧密耦合。 管理器与存储分离,这意味着,你可以在不更改应用程序代码的情况下替换持久性机制(配置除外)。
下图显示了 web 应用如何与管理器进行交互,同时存储与数据访问层交互。
若要创建自定义存储提供程序,请创建数据源、数据访问层以及与此数据访问层交互的存储类(上图中的绿色和灰色框)。 不需要自定义管理器或与之交互的应用程序代码(上面的蓝色框)。
在创建 UserManager
或 RoleManager
的新实例时,提供 user 类的类型并将 store 类的实例作为参数传递。 此方法使您可以插入到 ASP.NET Core的自定义的类。
重新配置应用程序以使用新的存储提供程序演示如何使用自定义存储实例化 UserManager
和 RoleManager
。
ASP.NET Core标识数据类型进行详细介绍下列各节:
网站的已注册用户。 可以扩展IdentityUser类型,也可以将其用作你自己的自定义类型的示例。 无需从特定类型继承即可实现您自己的自定义标识存储解决方案。
有关表示用户标识的用户的一组语句(或声明)。 可以启用用户标识的更大表达式,而不能通过角色来实现。
有关在用户登录时要使用的外部身份验证提供程序(如 Facebook 或 Microsoft 帐户)的信息。 示例
站点的授权组。 包括角色 Id 和角色名称(如 "管理员" 或 "Employee")。 示例
本主题假定您熟悉要使用的持久性机制以及如何创建该机制的实体。 本主题不提供有关如何创建存储库或数据访问类; 详细信息使用 ASP.NET Core标识时,它提供了有关设计决策的一些建议。
设计自定义存储提供程序的数据访问层时,有很多自由。 你只需为你打算在应用程序中使用的功能创建持久性机制。 例如,如果你未在应用中使用角色,则无需为角色或用户角色关联创建存储。 你的技术和现有基础结构可能需要的 ASP.NET Core标识的默认实现不同的结构。 在数据访问层中,提供用于处理存储实现结构的逻辑。
数据访问层提供逻辑来将数据从 ASP.NET Core标识保存到数据源。 自定义存储提供程序的数据访问层可能包含以下类来存储用户和角色信息。
封装信息以连接到永久性机制并执行查询。 几个数据类需要此类的实例,通常通过依赖关系注入来提供此类实例。 示例。
存储和检索用户信息(例如用户名和密码哈希)。 示例
存储和检索角色信息(如角色名称)。 示例
存储和检索用户声明信息(如声明类型和值)。 示例
存储和检索用户登录信息(如外部身份验证提供程序)。 示例
存储和检索为哪些用户分配了哪些角色。 示例
提示: 仅实现你打算在应用程序中使用的类。
在数据访问类中,提供代码来执行持久性机制的数据操作。 例如,在自定义提供程序中,你可能具有以下代码,以便在store类中创建新用户:
public async Task<IdentityResult> CreateAsync(ApplicationUser user, CancellationToken cancellationToken = default(CancellationToken)) { cancellationToken.ThrowIfCancellationRequested(); if (user == null) throw new ArgumentNullException(nameof(user)); return await _usersTable.CreateAsync(user); }
用于创建用户的实现逻辑位于 _usersTable.CreateAsync
方法中,如下所示。
实现存储提供程序时,创建一个与IdentityUser 类等效的用户类。
用户类至少必须包含 Id
和 UserName
属性。
IdentityUser
类定义 UserManager
在执行请求的操作时调用的属性。 Id
属性的默认类型是字符串,但您可以从 IdentityUser<TKey, TUserClaim, TUserRole, TUserLogin, TUserToken>
继承,并指定不同的类型。 框架需要存储实现来处理数据类型转换。
创建一个 UserStore
类,该类提供用户的所有数据操作的方法。 此类与UserStore<TUser>类等效。 在 UserStore
类中,实现所需的 IUserStore<TUser>
和可选接口。 根据应用中提供的功能,选择要实现的可选接口。
可选接口继承自 IUserStore<TUser>
。 可以在示例应用中查看部分实现的示例用户存储。
在 UserStore
类中,你将使用你创建的数据访问类来执行操作。 使用依赖关系注入传入它们。 例如,在使用 Dapper 实现的 SQL Server 中,UserStore
类具有 CreateAsync
方法,该方法使用 DapperUsersTable
的实例插入新记录:
public async Task<IdentityResult> CreateAsync(ApplicationUser user) { string sql = "INSERT INTO dbo.CustomUser " + "VALUES (@id, @Email, @EmailConfirmed, @PasswordHash, @UserName)"; int rows = await _connection.ExecuteAsync(sql, new { user.Id, user.Email, user.EmailConfirmed, user.PasswordHash, user.UserName }); if(rows > 0) { return IdentityResult.Success; } return IdentityResult.Failed(new IdentityError { Description = $"Could not insert user {user.Email}." }); }
只需实现应用程序中所需的接口。 例如:
public class UserStore : IUserStore<IdentityUser>, IUserClaimStore<IdentityUser>, IUserLoginStore<IdentityUser>, IUserRoleStore<IdentityUser>, IUserPasswordStore<IdentityUser>, IUserSecurityStampStore<IdentityUser> { // interface implementations not shown }
Microsoft.AspNet.Identity.EntityFramework
命名空间包含IdentityUserClaim、 IdentityUserLogin和 IdentityUserRole 类的实现。 如果使用这些功能,可能需要创建自己版本的这些类并定义应用的属性。 但是,在执行基本操作(如添加或删除用户的声明)时,如果不将这些实体加载到内存中,则可能会更有效。 而后端存储类可以直接对数据源执行这些操作。 例如,UserStore.GetClaimsAsync
方法可以调用 userClaimTable.FindByUserId(user.Id)
方法来直接对该表执行查询并返回声明列表。
实现角色存储提供程序时,可以创建自定义角色类型。 它不需要实现特定接口,但它必须具有 Id
,并且通常具有 Name
属性。
下面是一个示例 role 类:
using System; namespace CustomIdentityProviderSample.CustomProvider { public class ApplicationRole { public Guid Id { get; set; } = Guid.NewGuid(); public string Name { get; set; } } }
你可以创建一个 RoleStore
类,该类提供针对角色的所有数据操作的方法。 此类与RoleStore<TRole>类等效。 在 RoleStore
类中,你可以实现 IRoleStore<TRole>
,还可以选择 IQueryableRoleStore<TRole>
接口。
RoleStore
,请创建实现 IRoleStore<TRole>
接口的类。实现存储提供程序后,可将应用程序配置为使用该访问接口。 如果你的应用程序使用了默认提供程序,请将其替换为你的自定义提供程序。
Microsoft.AspNetCore.EntityFramework.Identity
NuGet 包。Microsoft.AspNetCore.EntityFramework.Identity
的所有引用。ConfigureServices
方法中,将 AddIdentity
方法更改为使用您的自定义类型。 出于此目的,你可以创建自己的扩展方法。 有关示例,请参阅IdentityServiceCollectionExtensions 。RoleManager
更新为使用你 RoleStore
的类。示例:
public void ConfigureServices(IServiceCollection services) { // Add identity types services.AddIdentity<ApplicationUser, ApplicationRole>() .AddDefaultTokenProviders(); // Identity Services services.AddTransient<IUserStore<ApplicationUser>, CustomUserStore>(); services.AddTransient<IRoleStore<ApplicationRole>, CustomRoleStore>(); string connectionString = Configuration.GetConnectionString("DefaultConnection"); services.AddTransient<SqlConnection>(e => new SqlConnection(connectionString)); services.AddTransient<DapperUsersTable>(); // additional configuration }