Работает ли новая платформа идентификации ASP.NET MVC без Entity Framework и SQL Server?

Я новичок в ASP.NET MVC 5 и поэтому стараюсь использовать его как можно больше, чтобы изучить его на практике.

Поэтому я думаю об использовании новой реализации OWIN ASP.NET MVC для реализации аутентификации и авторизации моего проекта. Тем не менее, я строю проект таким образом, чтобы он мог работать с различными типами баз данных.

До сих пор я использовал общие элементы ADO.NET (например, DbDataReader и т. Д.), И я отказался от использования ORM. Поэтому мне интересно, могу ли я продолжить использование новой системы идентификации ASP.NET или я буду привязан к Entity Framework и SQL Server, если я это сделаю?


person Aref    schedule 26.03.2014    source источник


Ответы (4)


Не все так просто. Не так уж и сложно.

Вам нужно будет написать свою собственную реализацию:

  1. IUserStore<TUser>
  2. IUserPasswordStore<TUser>
  3. IUserTwoFactorStore<TUser>
  4. IUserClaimStore<TUser>
  5. IRoleStore<TRole>
  6. IUserSecurityStampStore<TUser, string>
  7. IUserRoleStore<TUser, string>
  8. UserManager<TUser>

Затем создайте свою собственную пользовательскую реализацию из IUser<TKey>, например:

public class MyUser : IUser<string>
{
    public string Id { get; set; }
    public string UserName { get; set; }
}

Наконец, из NuGet удалите AspNet.Identity.EntityFramework, который также удалит EntityFramework, если вы не используете его где-либо еще.

Если ваш код ломается, перепишите его, чтобы использовать свои собственные реализации.

Кончик

Создайте MyUserRepository, который реализует элементы с 1 по 7.

Затем создайте MyUserManager, который реализует элемент 8.

Будет чертовски легко подключить это вместо классов AspNet.Identity.EntityFramework по умолчанию.

person Anderson Matos    schedule 27.03.2014
comment
IRoleStore и IUserStore реализуют метод FindByIdAsync с той же подписью. Может быть, было бы неплохо реализовать IRoleStore и IUserRoleStore в их собственных репозиториях? - person Joisey Mike; 04.08.2015
comment
Шаг за шагом к последней версии ... markjohnson.io / article / - person McGuireV10; 12.12.2017

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

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

Он не только подробно описывает, какие интерфейсы необходимо реализовать, но и подробно описывает, как их реализовать, и дает ссылки на примеры кода для реальной реализации MySQL.

person Tim Goyer    schedule 30.10.2014

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

public partial class Startup
{
    public static OAuthAuthorizationServerOptions OAuthOptions { get; private set; }
    public void ConfigureAuth(IAppBuilder app)
    {
        // Configure the db context, user manager and role manager to use a single instance per request
        app.CreatePerOwinContext(ApplicationDbContext.Create);
        app.CreatePerOwinContext<ApplicationUserManager>(ApplicationUserManager.Create);

        // Enable the application to use a cookie to store information for the signed in user
        // and to use a cookie to temporarily store information about a user logging in with a third party login provider
        app.UseCookieAuthentication(new CookieAuthenticationOptions());
        app.UseExternalSignInCookie(DefaultAuthenticationTypes.ExternalCookie);

        // Configure the application for OAuth based flow
        OAuthOptions = new OAuthAuthorizationServerOptions
        {
            TokenEndpointPath = new PathString("/Token"),
            Provider = new ApplicationOAuthProvider(),
            AuthorizeEndpointPath = new PathString("/api/Account/ExternalLogin"),
            AccessTokenExpireTimeSpan = TimeSpan.FromDays(14),
            // In production mode set AllowInsecureHttp = false
            AllowInsecureHttp = true
        };

        // Enable the application to use bearer tokens to authenticate users
        app.UseOAuthBearerTokens(OAuthOptions);
    }
}

public class ApplicationUser : IUser
{
    public ApplicationUser()
    {
        Id = Guid.NewGuid().ToString();
        Roles = new List<string>();
    }

    public virtual string Email { get; set; }
    public List<string> Roles { get; set; }
    public virtual string Password { get; set; }
    public DateTime CreatedTime { get; set; }

    public DateTime UpdatedTime { get; set; }

    public string Id { get; }
    public string UserName { get; set; }

    public virtual void AddRole(string role)
    {
        Roles.Add(role);
    }

    public virtual void RemoveRole(string role)
    {
        Roles.Remove(role);
    }
}

public class ApplicationUserManager : UserManager<ApplicationUser>
    {
        public ApplicationUserManager(IUserStore<ApplicationUser> store)
            : base(store)
        {
        }

        public static ApplicationUserManager Create(IdentityFactoryOptions<ApplicationUserManager> options,
            IOwinContext context)
        {
            var manager =
                new ApplicationUserManager(
                    new UserStoreService<ApplicationUser>(context.Get<ApplicationDbContext>().Users));
            manager.PasswordHasher = new FusionPasswordHasher();

            // Configure validation logic for passwords
            manager.PasswordValidator = new PasswordValidator
            {
                RequiredLength = 6,
                RequireNonLetterOrDigit = false,
                RequireDigit = false,
                RequireLowercase = false,
                RequireUppercase = false
            };

            // Configure user lockout defaults
            manager.UserLockoutEnabledByDefault = true;
            manager.DefaultAccountLockoutTimeSpan = TimeSpan.FromMinutes(5);
            manager.MaxFailedAccessAttemptsBeforeLockout = 5;

            var dataProtectionProvider = options.DataProtectionProvider;
            if (dataProtectionProvider != null)
                manager.UserTokenProvider =
                    new DataProtectorTokenProvider<ApplicationUser>(dataProtectionProvider.Create("ASP.NET Identity"));
            return manager;
        }

        public virtual async Task<IdentityResult> AddUserToRolesAsync(string userId, IList<string> roles)
        {
            var userRoleStore = (IUserRoleStore<ApplicationUser, string>) Store;

            var user = await FindByIdAsync(userId).ConfigureAwait(false);
            if (user == null)
                throw new InvalidOperationException("Invalid user Id");

            var userRoles = await userRoleStore.GetRolesAsync(user).ConfigureAwait(false);
            // Add user to each role using UserRoleStore
            foreach (var role in roles.Where(role => !userRoles.Contains(role)))
                await userRoleStore.AddToRoleAsync(user, role).ConfigureAwait(false);

            // Call update once when all roles are added
            return await UpdateAsync(user).ConfigureAwait(false);
        }

        public virtual async Task<IdentityResult> RemoveUserFromRolesAsync(string userId, IList<string> roles)
        {
            var userRoleStore = (IUserRoleStore<ApplicationUser, string>) Store;

            var user = await FindByIdAsync(userId).ConfigureAwait(false);
            if (user == null)
                throw new InvalidOperationException("Invalid user Id");

            var userRoles = await userRoleStore.GetRolesAsync(user).ConfigureAwait(false);
            // Remove user to each role using UserRoleStore
            foreach (var role in roles.Where(userRoles.Contains))
                await userRoleStore.RemoveFromRoleAsync(user, role).ConfigureAwait(false);

            // Call update once when all roles are removed
            return await UpdateAsync(user).ConfigureAwait(false);
        }
    }

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

public class ApplicationDbContext : IDisposable
{
    private ApplicationDbContext(IList<ApplicationUser> users)
    {
        Users = users;
    }

    public IList<ApplicationUser> Users { get; set; }

    public void Dispose()
    {
    }

    public static ApplicationDbContext Create()
    {
        //You can use any database and hook it here

        var users = new List<ApplicationUser>
        {
            new ApplicationUser
            {
                UserName = "[email protected]",
                Email = "[email protected]",
                Password = "test",
                Roles = new List<string> {"Admin", "Admin2"}
            },
            new ApplicationUser
            {
                UserName = "[email protected]",
                Email = "[email protected]",
                Password = "test2",
                Roles = new List<string> {"Admin"}
            }
        };

        return new ApplicationDbContext(users);
    }
}

public class ApplicationOAuthProvider : OAuthAuthorizationServerProvider
{
    public override async Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context)
    {
        var userManager = context.OwinContext.GetUserManager<ApplicationUserManager>();

        var user = await userManager.FindAsync(context.UserName.ToLower(), context.Password);

        if (user == null)
        {
            context.SetError("invalid_grant", "The user name or password is incorrect.");
            return;
        }

        try
        {
            var oAuthIdentity = await userManager.CreateIdentityAsync(user, context.Options.AuthenticationType);
            var cookiesIdentity = await userManager.CreateIdentityAsync(user,
                CookieAuthenticationDefaults.AuthenticationType);
            var props = new AuthenticationProperties(new Dictionary<string, string>
            {
                {
                    "client_id", context.ClientId == null ? string.Empty : context.ClientId
                },
                {
                    "userName", context.UserName
                }
            });
            var ticket = new AuthenticationTicket(oAuthIdentity, props);
            context.Validated(ticket);
            context.Request.Context.Authentication.SignIn(cookiesIdentity);
        }
        catch (Exception ex)
        {
            Trace.TraceError("FUSION Error ::: " + ex.Message + ex.InnerException);
            Trace.TraceError(ex.Message);
        }
    }

    public override Task TokenEndpoint(OAuthTokenEndpointContext context)
    {
        foreach (var property in context.Properties.Dictionary)
            if (property.Value != null)
                context.AdditionalResponseParameters.Add(property.Key, property.Value);

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

    public override Task ValidateClientAuthentication(OAuthValidateClientAuthenticationContext context)
    {
        // Resource owner password credentials does not provide a client ID.
        if (context.ClientId == null)
            context.Validated();

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

public class AppPasswordHasher : IPasswordHasher
{
    public string HashPassword(string password)
    {
        return password;
    }

    public PasswordVerificationResult VerifyHashedPassword
        (string hashedPassword, string providedPassword)
    {
        if (hashedPassword == HashPassword(providedPassword))
            return PasswordVerificationResult.Success;
        return PasswordVerificationResult.Failed;
    }
}

Метод вроде «FindByNameAsync»; где вам нужно читать пользователя из базы данных по запросу / входу в систему

public class UserStoreService<TUser> : IUserStore<TUser>,
    IUserPasswordStore<TUser>,
    IUserRoleStore<TUser>
    where TUser : ApplicationUser
{
    private readonly IList<TUser> _users;


    public UserStoreService(IList<TUser> users)
    {
        _users = users;
    }

    public virtual Task SetPasswordHashAsync(TUser user, string passwordHash)
    {
        user.Password = passwordHash;
        return Task.FromResult(0);
    }

    public virtual Task<string> GetPasswordHashAsync(TUser user)
    {
        return Task.FromResult(user.Password);
    }

    public virtual Task<bool> HasPasswordAsync(TUser user)
    {
        return Task.FromResult(user.Password != null);
    }

    public virtual Task AddToRoleAsync(TUser user, string roleName)
    {
        user.AddRole(roleName);
        return Task.FromResult(0);
    }

    public virtual Task RemoveFromRoleAsync(TUser user, string roleName)
    {
        user.RemoveRole(roleName);
        return Task.FromResult(0);
    }

    public virtual Task<IList<string>> GetRolesAsync(TUser user)
    {
        return Task.FromResult((IList<string>) user.Roles);
    }

    public virtual Task<bool> IsInRoleAsync(TUser user, string roleName)
    {
        return Task.FromResult(user.Roles.Contains(roleName));
    }

    public virtual void Dispose()
    {
    }

    public virtual Task CreateAsync(TUser user)
    {
        user.CreatedTime = DateTime.Now;
        user.UpdatedTime = DateTime.Now;
        _users.Add(user);
        return Task.FromResult(true);
    }

    public virtual Task UpdateAsync(TUser user)
    {
        // todo should add an optimistic concurrency check
        user.UpdatedTime = DateTime.Now;
        _users.Remove(user);
        _users.Add(user);
        return Task.FromResult(true);
    }

    public virtual Task DeleteAsync(TUser user)
    {
        return Task.FromResult(_users.Remove(user));
    }

    public virtual Task<TUser> FindByIdAsync(string userId)
    {
        return Task.FromResult(_users.FirstOrDefault(u => u.Id == userId));
    }

    public virtual Task<TUser> FindByNameAsync(string userName)
    {
        // todo exception on duplicates? or better to enforce unique index to ensure this
        return Task.FromResult(_users.FirstOrDefault(u => u.Email == userName));
    }
}

[Authorize(Roles = "Admin")]
public class RolesController : ApiController
{
    public IEnumerable<string> Get()
    {
        return new[] {"value3", "value4"};
    }
}

Исходный код (github)

person Narottam Goyal    schedule 21.01.2017

По умолчанию он привязан к Entity Framework и SQL Server, но вы можете легко подключить другие хранилища данных, такие как SharePoint, службу таблиц хранилища Windows Azure, базы данных NoSQL и т. Д., И вы можете сохранить контроль над схемой базы данных.

Дополнительная информация
Введение в ASP.NET Identity

person Robert Harvey    schedule 26.03.2014