Обзор пользовательских поставщиков хранилищ для ASP.NET Identity

от Tom фитзмаккен

ASP.NET Identity — это расширяемая система, которая позволяет создать собственный поставщик хранилища и подключить его к приложению без повторной работы приложения. В этом разделе описывается создание настраиваемого поставщика хранилища для ASP.NET Identity. В нем рассматриваются важные понятия создания собственного поставщика хранилища, но не пошаговое руководство по реализации пользовательского поставщика хранилища.

Пример реализации пользовательского поставщика хранилища см. в статье реализация пользовательского поставщика хранилища ASP.NET Identity MySQL.

Этот раздел был обновлен для ASP.NET Identity 2,0.

Версии программного обеспечения, используемые в этом руководстве

  • Visual Studio 2013 с обновлением 2
  • ASP.NET Identity 2

Введение

По умолчанию ASP.NET Identity система хранит сведения о пользователях в базе данных SQL Server и использует Entity Framework Code First для создания базы данных. Для многих приложений такой подход хорошо работает. Однако вы можете использовать другой тип механизма сохранения, например хранилище таблиц Azure, или же таблицы базы данных с совершенно отличной структурой по сравнению с реализацией по умолчанию. В любом случае можно написать настраиваемый поставщик для механизма хранения и подключить его к приложению.

ASP.NET Identity включен по умолчанию во многих шаблонах Visual Studio 2013. Вы можете получить обновления для ASP.NET Identity с помощью пакета NuGet Microsoft AspNet Identity EntityFramework.

Этот раздел включает следующие подразделы:

Общие сведения об архитектуре

ASP.NET Identity состоит из классов, именуемых диспетчерами и магазинами. Руководители — это высокоуровневые классы, которые разработчик приложений использует для выполнения таких операций, как создание пользователя в системе ASP.NET Identity. Магазины — это классы более низкого уровня, указывающие, как сохраняются сущности, например пользователи и роли. Хранилища тесно связаны с механизмом сохранения, но менеджеры отделяются от хранилищ. Это означает, что вы можете заменить механизм сохраняемости, не нарушая работу всего приложения.

На следующей схеме показано, как веб-приложение взаимодействует с менеджерами, и сохраняет взаимодействие с уровнем доступа к данным.

Чтобы создать настраиваемый поставщик хранилища для ASP.NET Identity, необходимо создать источник данных, уровень доступа к данным и классы хранилища, взаимодействующие с этим уровнем доступа к данным. Вы можете продолжить использовать те же API-интерфейсы диспетчера для выполнения операций с данными на пользователя, но теперь эти данные сохраняются в другой системе хранения.

Настраивать классы Manager не требуется, так как при создании нового экземпляра UserManager или RoleManager вы предоставляете тип класса User и передаете экземпляр класса Store в качестве аргумента. Такой подход позволяет подключать пользовательские классы к существующей структуре. Вы узнаете, как создать экземпляр UserManager и RoleManager с вашими настроенными классами хранилища в разделе перенастройка приложения для использования нового поставщика хранилища.

Общие сведения о хранимых данных

Для реализации пользовательского поставщика хранилища необходимо понимать типы данных, используемых в ASP.NET Identity, а также определить, какие функции относятся к вашему приложению.

Данные Description
Пользователи Зарегистрированные пользователи вашего веб-сайта. Включает идентификатор пользователя и имя пользователя. Может включать хэшированный пароль, если пользователи входят в систему с учетными данными, специфическими для вашего сайта (вместо использования учетных данных с внешнего сайта, например Facebook), и метки безопасности, указывающей, изменились ли какие-либо изменения в учетных данных пользователя. Может также включать адрес электронной почты, номер телефона, включение двухфакторной проверки подлинности, текущее число неудачных попыток входа и заблокирована ли учетная запись.
Утверждения пользователей Набор инструкций (или утверждений) о пользователе, который представляет удостоверение пользователя. Можно включить большее выражение удостоверения пользователя, чем можно достичь с помощью ролей.
Имена входа пользователей Сведения о внешнем поставщике проверки подлинности (например, Facebook), который будет использоваться для входа пользователя.
Роли Группы авторизации для сайта. Содержит идентификатор роли и имя роли (например, "admin" или "Employee").

Создание уровня доступа к данным

В этом разделе предполагается, что вы знакомы с механизмом сохраняемости, который вы собираетесь использовать, и как создавать сущности для этого механизма. В этом разделе не приводятся сведения о создании репозиториев или классов доступа к данным. Вместо этого он предоставляет некоторые рекомендации по архитектуре, которые необходимо принять при работе с ASP.NET Identity.

При проектировании репозиториев для настроенного поставщика хранилища у вас есть масса свободы. Для компонентов, которые предполагается использовать в приложении, необходимо создать только репозитории. Например, если вы не используете роли в приложении, вам не нужно создавать хранилище для ролей или ролей пользователей. Для вашей технологии и существующей инфраструктуры может потребоваться структура, которая сильно отличается от реализации по умолчанию ASP.NET Identity. На уровне доступа к данным вы предоставляете логику для работы со структурой репозиториев.

Сведения о реализации репозиториев данных в MySQL для ASP.NET Identity 2,0 см. в разделе мисклидентити. SQL.

На уровне доступа к данным вы предоставляете логику для сохранения данных из ASP.NET Identity в источнике данных. Уровень доступа к данным для настроенного поставщика хранилища может включать следующие классы для хранения сведений о пользователях и ролях.

Class Description Пример
Контекст Инкапсулирует сведения для подключения к механизму сохранения и выполнения запросов. Этот класс является центральным для уровня доступа к данным. Другим классам данных потребуется экземпляр этого класса для выполнения своих операций. Кроме того, вы инициализируйте классы хранилища с помощью экземпляра этого класса. мисклдатабасе
Хранилище пользователя Сохраняет и извлекает сведения о пользователе (например, имя пользователя и хэш пароля). Усертабле (MySQL)
Хранилище ролей Сохраняет и извлекает сведения о ролях (например, имя роли). Ролетабле (MySQL)
Хранилище Усерклаимс Сохраняет и извлекает сведения о пользовательской заявке (например, тип и значение утверждения). Усерклаимстабле (MySQL)
Хранилище Усерлогинс Хранит и получает сведения для входа пользователя (например, внешний поставщик проверки подлинности). Усерлогинстабле (MySQL)
Хранилище UserRole Сохраняет и извлекает роли, назначенные пользователю. Усерролетабле (MySQL)

Опять же, достаточно реализовать классы, которые будут использоваться в приложении.

В классах доступа к данным вы предоставляете код для выполнения операций с данными в конкретном механизме сохраняемости. Например, в реализации MySQL класс Усертабле содержит метод для вставки новой записи в таблицу базы данных Users. Переменная с именем _database является экземпляром класса Мисклдатабасе.

public int Insert(TUser user)
{
    string commandText = @"Insert into Users (UserName, Id, PasswordHash, SecurityStamp,Email,EmailConfirmed,PhoneNumber,PhoneNumberConfirmed, AccessFailedCount,LockoutEnabled,LockoutEndDateUtc,TwoFactorEnabled)
        values (@name, @id, @pwdHash, @SecStamp,@email,@emailconfirmed,@phonenumber,@phonenumberconfirmed,@accesscount,@lockoutenabled,@lockoutenddate,@twofactorenabled)";
    Dictionary<string, object> parameters = new Dictionary<string, object>();
    parameters.Add("@name", user.UserName);
    parameters.Add("@id", user.Id);
    parameters.Add("@pwdHash", user.PasswordHash);
    parameters.Add("@SecStamp", user.SecurityStamp);
    parameters.Add("@email", user.Email);
    parameters.Add("@emailconfirmed", user.EmailConfirmed);
    parameters.Add("@phonenumber", user.PhoneNumber);
    parameters.Add("@phonenumberconfirmed", user.PhoneNumberConfirmed);
    parameters.Add("@accesscount", user.AccessFailedCount);
    parameters.Add("@lockoutenabled", user.LockoutEnabled);
    parameters.Add("@lockoutenddate", user.LockoutEndDateUtc);
    parameters.Add("@twofactorenabled", user.TwoFactorEnabled);

    return _database.Execute(commandText, parameters);
}

После создания классов доступа к данным необходимо создать классы хранения, которые вызывают конкретные методы на уровне доступа к данным.

Настройка класса User

При реализации собственного поставщика хранилища необходимо создать класс пользователя, эквивалентный классу идентитюсер в пространстве имен Microsoft. ASP. NET. Identity. EntityFramework :

На следующей схеме показан класс Идентитюсер, который необходимо создать, и интерфейс для реализации в этом классе.

Интерфейс >IUser<TKey определяет свойства, которые UserManager пытается вызвать при выполнении запрошенных операций. Интерфейс содержит два свойства: ID и UserName. Интерфейс >IUser<TKey позволяет указать тип ключа для пользователя с помощью универсального параметра TKey . Тип свойства ID соответствует значению параметра TKey.

Платформа удостоверений также предоставляет интерфейс IUser (без универсального параметра), если требуется использовать строковое значение для ключа.

Класс Идентитюсер реализует IUser и содержит любые дополнительные свойства или конструкторы для пользователей на вашем веб-сайте. В следующем примере показан класс Идентитюсер, который использует целое число для ключа. Поле идентификатора устанавливается в значение int в соответствии со значением универсального параметра.

public class IdentityUser : IUser<int>
{
    public IdentityUser() { ... }
    public IdentityUser(string userName) { ... }
    public int Id { get; set; }
    public string UserName { get; set; }
    // can also define optional properties such as:
    //    PasswordHash
    //    SecurityStamp
    //    Claims
    //    Logins
    //    Roles
}

Полную реализацию см. в разделе идентитюсер (MySQL).

Настройка хранилища пользователей

Также создается класс UserStore, предоставляющий методы для всех операций с данными пользователя. Этот класс эквивалентен классу UserStore<тусер> в пространстве имен Microsoft. ASP. NET. Identity. EntityFramework . В классе UserStore вы реализуете IUserStore<Тусер, TKey> и любые дополнительные интерфейсы. Вы можете выбрать, какие дополнительные интерфейсы следует реализовать на основе функциональных возможностей, которые вы хотите предоставить в приложении.

На следующем рисунке показан класс UserStore, который необходимо создать, и соответствующие интерфейсы.

Шаблон проекта по умолчанию в Visual Studio содержит код, который предполагает, что многие из дополнительных интерфейсов были реализованы в хранилище пользователя. Если вы используете шаблон по умолчанию с настроенным хранилищем пользователей, необходимо либо реализовать дополнительные интерфейсы в хранилище пользователя, либо изменить код шаблона, чтобы он больше не вызывал методы в интерфейсах, которые не были реализованы.

В следующем примере показан простой класс хранилища пользователя. Универсальный параметр Тусер принимает тип класса пользователя, который обычно является классом идентитюсер, который вы определили. Универсальный параметр TKey принимает тип ключа пользователя.

public class UserStore : IUserStore<IdentityUser, int>
{
    public UserStore() { ... }
    public UserStore(ExampleStorage database) { ... }
    public Task CreateAsync(IdentityUser user) { ... }
    public Task DeleteAsync(IdentityUser user) { ... }
    public Task<IdentityUser> FindByIdAsync(int userId) { ... }
    public Task<IdentityUser> FindByNameAsync(string userName) { ... }
    public Task UpdateAsync(IdentityUser user) { ... }
    public void Dispose() { ... }
}

В этом примере конструктор, принимающий параметр с именем Database типа ексампледатабасе, является лишь иллюстрацией того, как передать в класс доступа к данным. Например, в реализации MySQL этот конструктор принимает параметр типа Мисклдатабасе.

В классе UserStore используются классы доступа к данным, созданные для выполнения операций. Например, в реализации MySQL класс UserStore имеет метод CreateAsync, который использует экземпляр Усертабле для вставки новой записи. Метод INSERT объекта усертабле — это тот же метод, который был показан в предыдущем разделе.

public Task CreateAsync(IdentityUser user)
{
    if (user == null) {
        throw new ArgumentNullException("user");
    }

    userTable.Insert(user);

    return Task.FromResult<object>(null);
}

Интерфейсы, реализуемые при настройке хранилища пользователей

На следующем рисунке показаны дополнительные сведения о функциональных возможностях, определенных в каждом интерфейсе. Все необязательные интерфейсы наследуют от IUserStore.

  • IUserStore
    Интерфейс IUserStore<Тусер, TKey> является единственным интерфейсом, который необходимо реализовать в хранилище пользователя. Он определяет методы для создания, обновления, удаления и получения пользователей.

  • IUserClaimStore
    Интерфейс IUserClaimStore<Тусер, TKey> определяет методы, которые необходимо реализовать в хранилище пользователя для включения заявок пользователя. Он содержит методы, а также добавляет, удаляет и извлекает утверждения пользователей.

  • иусерлогинсторе
    Иусерлогинсторе<Тусер, TKey> определяет методы, которые необходимо реализовать в хранилище пользователя для включения внешних поставщиков проверки подлинности. Он содержит методы для добавления, удаления и получения имен входа пользователей, а также метод для получения пользователя на основе сведений об имени входа.

  • IUserRoleStore
    Интерфейс IUserRoleStore<TKey, тусер> определяет методы, которые необходимо реализовать в хранилище пользователя, чтобы сопоставлять пользователя с ролью. Он содержит методы для добавления, удаления и получения ролей пользователя, а также метод для проверки того, назначен ли пользователю роль.

  • IUserPasswordStore
    Интерфейс IUserPasswordStore<Тусер, TKey> определяет методы, которые необходимо реализовать в хранилище пользователя для сохранения хэшированных паролей. Он содержит методы для получения и установки хэшированного пароля, а также метод, который указывает, установил ли пользователь пароль.

  • иусерсекуритистампсторе
    Интерфейс >иусерсекуритистампсторе<Тусер, TKey определяет методы, которые необходимо реализовать в хранилище пользователя, чтобы использовать метку безопасности для указания, изменились ли сведения об учетной записи пользователя. Эта метка обновляется, когда пользователь изменяет пароль или добавляет или удаляет имена входа. Он содержит методы для получения и установки метки безопасности.

  • IUserTwoFactorStore
    Интерфейс >IUserTwoFactorStore<Тусер, TKey определяет методы, которые необходимо реализовать для реализации двухфакторной проверки подлинности. Он содержит методы для получения и настройки проверки подлинности, включенной для пользователя.

  • иусерфоненумберсторе
    Интерфейс иусерфоненумберсторе<Тусер, TKey> определяет методы, которые необходимо реализовать для хранения телефонных номеров пользователей. Он содержит методы для получения и установки номера телефона, а также сведения о подтверждении номера телефона.

  • иусеремаилсторе
    Интерфейс иусеремаилсторе<Тусер, TKey> определяет методы, которые необходимо реализовать для хранения адресов электронной почты пользователей. Он содержит методы для получения и установки адреса электронной почты, а также сведения о том, подтверждено ли сообщение электронной почты.

  • иусерлоккаутсторе
    Интерфейс иусерлоккаутсторе<Тусер, TKey> определяет методы, которые необходимо реализовать для хранения сведений о блокировке учетной записи. Он содержит методы для получения текущего количества неудачных попыток доступа, получения и задания возможности блокировки учетной записи, получения и установки даты окончания блокировки, увеличения числа неудачных попыток и сброса количества неудачных попыток.

  • IQueryableUserStore
    Интерфейс IQueryableUserStore<Тусер, TKey> определяет члены, которые необходимо реализовать для предоставления запрашиваемого хранилища пользователя. Он содержит свойство, которое содержит запрашиваемых пользователей.

    Вы реализуете интерфейсы, необходимые в приложении; Например, интерфейсы IUserClaimStore, Иусерлогинсторе, IUserRoleStore, IUserPasswordStore и Иусерсекуритистампсторе, как показано ниже.

public class UserStore : IUserStore<IdentityUser, int>,
                         IUserClaimStore<IdentityUser, int>,
                         IUserLoginStore<IdentityUser, int>,
                         IUserRoleStore<IdentityUser, int>,
                         IUserPasswordStore<IdentityUser, int>,
                         IUserSecurityStampStore<IdentityUser, int>
{
    // interface implementations not shown
}

Полную реализацию (включая все интерфейсы) см. в разделе UserStore (MySQL).

Идентитюсерклаим, Идентитюсерлогин и Идентитюсерроле

Пространство имен Microsoft. AspNet. Identity. EntityFramework содержит реализации классов идентитюсерклаим, идентитюсерлогини идентитюсерроле . При использовании этих функций может потребоваться создать собственные версии этих классов и определить свойства приложения. Однако иногда эффективнее не загружать эти сущности в память при выполнении основных операций (например, при добавлении или удалении утверждения пользователя). Вместо этого классы хранилища серверной части могут выполнять эти операции непосредственно в источнике данных. Например, метод UserStore. Жетклаимсасинк () может вызвать Усерклаимтабле. Финдбюсерид (пользователь. ID) для выполнения запроса к этой таблице напрямую и возврата списка утверждений.

public Task<IList<Claim>> GetClaimsAsync(IdentityUser user)
{
    ClaimsIdentity identity = userClaimsTable.FindByUserId(user.Id);
    return Task.FromResult<IList<Claim>>(identity.Claims.ToList());
}

Настройка класса Role

При реализации собственного поставщика хранилища необходимо создать класс роли, эквивалентный классу идентитироле в пространстве имен Microsoft. ASP. NET. Identity. EntityFramework :

На следующей схеме показан класс Идентитироле, который необходимо создать, и интерфейс для реализации в этом классе.

Интерфейс >ироле<TKey определяет свойства, которые метод roleManager пытается вызвать при выполнении запрошенных операций. Интерфейс содержит два свойства — ID и Name. Интерфейс >ироле<TKey позволяет указать тип ключа для роли с помощью универсального параметра TKey . Тип свойства ID соответствует значению параметра TKey.

Платформа удостоверений также предоставляет интерфейс ироле (без универсального параметра), если требуется использовать строковое значение для ключа.

В следующем примере показан класс Идентитироле, который использует целое число для ключа. Поле идентификатора устанавливается в значение int в соответствии со значением универсального параметра.

public class IdentityRole : IRole<int>
{
    public IdentityRole() { ... }
    public IdentityRole(string roleName) { ... }
    public int Id { get; set; }
    public string Name { get; set; }
}

Полную реализацию см. в разделе идентитироле (MySQL).

Настройка хранилища ролей

Также создается класс Ролесторе, предоставляющий методы для всех операций с данными в ролях. Этот класс эквивалентен классу ролесторе<троле> в пространстве имен Microsoft. ASP. NET. Identity. EntityFramework. В классе Ролесторе вы реализуете иролесторе<троле, tkey> и, при необходимости, интерфейс икуеряблеролесторе<троле, TKey> .

В следующем примере показан класс хранилища ролей. Универсальный параметр троле принимает тип класса роли, который обычно является классом Идентитироле, который вы определили. Универсальный параметр TKey принимает тип ключа роли.

public class RoleStore : IRoleStore<IdentityRole, int>
{
    public RoleStore() { ... }
    public RoleStore(ExampleStorage database) { ... }
    public Task CreateAsync(IdentityRole role) { ... }
    public Task DeleteAsync(IdentityRole role) { ... }
    public Task<IdentityRole> FindByIdAsync(int roleId) { ... }
    public Task<IdentityRole> FindByNameAsync(string roleName) { ... }
    public Task UpdateAsync(IdentityRole role) { ... }
    public void Dispose() { ... }
}
  • Иролесторе<троле>
    Интерфейс иролесторе определяет методы для реализации в классе хранилища ролей. Он содержит методы для создания, обновления, удаления и извлечения ролей.

  • Ролесторе<троле>
    Чтобы настроить Ролесторе, создайте класс, реализующий интерфейс Иролесторе. Этот класс необходимо реализовать только в том случае, если требуется использовать роли в системе. Конструктор, принимающий параметр с именем Database типа ексампледатабасе, является только иллюстрацией того, как передать в класс доступа к данным. Например, в реализации MySQL этот конструктор принимает параметр типа Мисклдатабасе.

    Полную реализацию см. в разделе ролесторе (MySQL) .

Перенастроить приложение для использования нового поставщика хранилища

Вы реализовали новый поставщик хранилища. Теперь необходимо настроить приложение для использования этого поставщика хранилища. Если поставщик хранилища по умолчанию был добавлен в проект, необходимо удалить поставщика по умолчанию и заменить его своим поставщиком.

Замена поставщика хранилища по умолчанию в проекте MVC

  1. В окне Управление пакетами NuGet удалите пакет Microsoft ASP.NET Identity EntityFramework . Этот пакет можно найти, выполнив поиск в установленных пакетах для Identity. EntityFramework.
    вам будет предложено также удалить Entity Framework. Если он не нужен в других частях приложения, его можно удалить.

  2. В файле IdentityModels.cs в папке Models удалите или закомментируйте классы аппликатионусер и ApplicationDbContext . В приложении MVC можно удалить весь файл IdentityModels.cs. В приложении веб-форм удалите эти два класса, но убедитесь, что вы сохраняете вспомогательный класс, который также находится в файле IdentityModels.cs.

  3. Если поставщик хранилища находится в отдельном проекте, добавьте в веб-приложение ссылку на него.

  4. Замените все ссылки на using Microsoft.AspNet.Identity.EntityFramework; с помощью оператора using для пространства имен поставщика хранилища.

  5. В классе Startup.auth.CS измените метод конфигуреаус , чтобы он использовал единственный экземпляр соответствующего контекста.

    public void ConfigureAuth(IAppBuilder app)
    {
        app.CreatePerOwinContext(ExampleStorageContext.Create);
        app.CreatePerOwinContext(ApplicationUserManager.Create);
        ...
    
  6. В папке приложения_Пуск откройте IdentityConfig.CS. В классе Аппликатионусерманажер измените метод CREATE , чтобы он возвращал Диспетчер пользователей, использующий настроенное хранилище пользователя.

    public static ApplicationUserManager Create(IdentityFactoryOptions<ApplicationUserManager> options, IOwinContext context) 
    {
        var manager = new ApplicationUserManager(new UserStore(context.Get<ExampleStorageContext>()));
        ...
    }
    
  7. Замените все ссылки на аппликатионусер на идентитюсер.

  8. Проект по умолчанию содержит некоторые члены класса User, которые не определены в интерфейсе IUser. Например, email, PasswordHash и Женератеусеридентитясинк. Если у вашего класса пользователя нет этих членов, необходимо либо реализовать их, либо изменить код, использующий эти члены.

  9. Если вы создали какие бы то ни было экземпляры RoleManager, измените этот код для использования нового класса Ролесторе.

    var roleManager = new RoleManager<IdentityRole>(new RoleStore(context.Get<ExampleStorageContext>()));
    
  10. Проект по умолчанию предназначен для пользовательского класса, имеющего строковое значение для ключа. Если класс пользователя имеет другой тип ключа (например, целое число), необходимо изменить проект для работы с типом. См. раздел Изменение первичного ключа для пользователей в ASP.NET Identity.

  11. При необходимости добавьте строку подключения в файл Web. config.

Другие ресурсы