MVC 5 Identity без использования Entity Framework

Пример реализации авторизации в MVC 5 без использования штатной базы данных и Entity Framework.

В реальной жизни в качестве источника данных о пользователях может выступать не только база данных, но и веб-сервис, какой-нибудь Web API или вообще что-то другое совсем свое. Пример показывает, как использовать свой источник совместно со стандартными механизмами авторизации MVC 5.

 

Начнем с создания проекта MVC 5. Создаем проект типа ASP.NET Web Application (.NET Framework).

Создание проекта MVC5

Выбираем шаблон проекта — MVC.

Выбор шаблона проекта MVC5

Сразу удаляем из проекта библиотеки Entity Framework, чтобы они нас не смущали. Для этого в окне Solution Explorer кликаем правой кнопкой по проекту и выбираем пункт Manage NuGet Packages.... Находим и деинсталируем пакет Microsoft.AspNet.Identity.EntityFramework.

Удаление из проекта пакета Entity Framework

AccountService.cs

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

public class AccountService : IDisposable
{
    private readonly ApplicationUser[] _sampleUsers =
    {
        new ApplicationUser { Id = "alpha@mail.ru", UserName = "alpha@mail.ru", PasswordHash = "123456" },
        new ApplicationUser { Id = "beta@mail.ru", UserName = "beta@mail.ru", PasswordHash = "6543211" },
        new ApplicationUser { Id = "demo@mail.ru", UserName = "demo@mail.ru", PasswordHash = "456123" }
    };

    public static AccountService Create()
    {
        return new AccountService();
    }
        
    public async Task<ApplicationUser> Get(string userId)
    {
        var id = userId.ToLower();
        return _sampleUsers.First(u => u.Id.Equals(id));
    }

    public async Task<ApplicationUser> Find(string userName)
    {
        var name = userName.ToLower();
        return _sampleUsers.FirstOrDefault(u => u.UserName.Equals(name));
    }

    public void Dispose()
    {
    }
}

UserStore.cs

Самое сложное и интересное — создать собственный класс хранилища данных пользователей.

В классе реализованы только методы, необходимые для работы механизма авторизации пользователя. Все остальные методы не реализованы и порождают исключение NotImplementedException.

public class UserStore : IUserStore<ApplicationUser>,
    IUserLockoutStore<ApplicationUser, string>,
    IUserPasswordStore<ApplicationUser>,
    IUserTwoFactorStore<ApplicationUser, string>,
    IUserPhoneNumberStore<ApplicationUser, string>,
    IUserLoginStore<ApplicationUser>
{
    private readonly AccountService _service;


    public UserStore(AccountService service)
    {
        _service = service;
    }


    public void Dispose()
    {
    }
          
      
    public async Task<ApplicationUser> FindByIdAsync(string userId)
    {
        return await _service.Get(userId);
    }


    public async Task<ApplicationUser> FindByNameAsync(string userName)
    {
        return await _service.Find(userName);
    }


    public async Task<int> GetAccessFailedCountAsync(ApplicationUser user)
    {
        return 0;
    }


    public async Task<bool> GetLockoutEnabledAsync(ApplicationUser user)
    {
        return false;
    }


    public async Task<string> GetPasswordHashAsync(ApplicationUser user)
    {
        return Crypto.HashPassword(user.PasswordHash);
    }


    public async Task<bool> GetTwoFactorEnabledAsync(ApplicationUser user)
    {
        return false;
    }


    public async Task<string> GetPhoneNumberAsync(ApplicationUser user)
    {
        return user.PhoneNumber;
    }


    public async Task<IList<UserLoginInfo>> GetLoginsAsync(ApplicationUser user)
    {
        return (IList<UserLoginInfo>)new List<UserLoginInfo>
        {
            new UserLoginInfo("Custom", "None")
        };
    }

    ...
}

Models/IdentityModels.cs

Модель, описывающая пользователя (класс IdentityUser) «погибла» вместе с EF, поэтому нам необходимо создать свою модель пользователя, а точнее — подправить существующую.

Идем в файл Models/IdentityModels.cs.

Удаляем за ненадобностью класс ApplicationDbContext. У класса ApplicationUser убираем базовый класс, реализуем интерфейс IUser и добавляем необходимые свойства.

public class ApplicationUser : IUser
{
    public string Id { get; set; }
    public string UserName { get; set; }

    public string Email { get; set; }
    public string PasswordHash { get; set; }
    public string PhoneNumber { get; set; }


    public async Task<ClaimsIdentity> GenerateUserIdentityAsync(UserManager<ApplicationUser> manager)
    {
        // Note the authenticationType must match the one defined in CookieAuthenticationOptions.AuthenticationType
        var userIdentity = await manager.CreateIdentityAsync(this, DefaultAuthenticationTypes.ApplicationCookie);
        // Add custom user claims here
        return userIdentity;
    }
}

App_Start/Startup.Auth.cs

Далее, заменяем контекст EF своим сервисом.

Идем в файл App_Start/Startup.Auth.cs и заменяем строку

app.CreatePerOwinContext(ApplicationDbContext.Create);

на строку

app.CreatePerOwinContext(AccountService.Create);

Получаем следующий код:

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, user manager and signin manager to use a single instance per request
        app.CreatePerOwinContext(AccountService.Create);
        app.CreatePerOwinContext<ApplicationUserManager>(ApplicationUserManager.Create);
        app.CreatePerOwinContext<ApplicationSignInManager>(ApplicationSignInManager.Create);

        ...
    }
}

App_Start/IdentityConfig.cs

Идем в файл App_Start/IdentityConfig.cs и в методе Create класса ApplicationUserManager заменяем строку

var manager = new ApplicationUserManager(new UserStore<ApplicationUser>(context.Get<ApplicationDbContext>()));

на строку

var manager = new ApplicationUserManager(new UserStore(context.Get<AccountService>()));

Получаем слудующий код:

public class ApplicationUserManager : UserManager&lt;ApplicationUser&gt;
{
    ...

    public static ApplicationUserManager Create(IdentityFactoryOptions&lt;ApplicationUserManager&gt; options, IOwinContext context) 
    {
        var manager = new ApplicationUserManager(new UserStore(context.Get&lt;AccountService&gt;()));
        // Configure validation logic for usernames
        manager.UserValidator = new UserValidator<ApplicationUser>(manager)
        {
            AllowOnlyAlphanumericUserNames = false,
            RequireUniqueEmail = true
        };

        ...
    }
}

 

В результате всех этих манипуляций у нас должна заработать авторизация на сайте.

Главная страница сайта

Вводим наши тестовые данные в форму авторизации.

Форма авторизации

И видим, что авторизация прошла успешно. Имя пользователя отображается в верхнем правом углу и если нажать на него то открывается страница управления учетной записью.

Форма управления учетной записью

Исходный код

Полезные ссылки

About the author

Добавить комментарий

Сказать спасибо

Способ платежа:

Подписаться на обновления

Укажите свой e-mail чтобы получать уведомления о новых статьях.

Присоединиться к еще 2 подписчикам