web-dev-qa-db-ja.com

新しいASP.NET MVC IDフレームワークは、Entity FrameworkとSQL Serverなしで機能しますか?

私はASP.NET MVC 5を初めて使用するので、できる限りそれを使用して実践的に学習しようとしています。

したがって、ASP.NET MVCの新しいOWIN実装を使用して、プロジェクトの認証と承認を実装することを考えています。そうは言っても、私はプロジェクトをさまざまな種類のデータベースで動作できるように構築しています。

これまでのところ、汎用ADO.NET要素(DbDataReaderなど)を使用しており、ORMの使用を拒否しています。だから、ASP.NETの新しいIDシステムを使用して先に進むことができるのか、そうすればEntity FrameworkとSQL Serverにバインドされるのか疑問に思っています。

34
Aref

それほど単純ではありません。それほど難しくもありません。

次のカスタム実装を作成する必要があります。

  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も削除されます。

コードが破損した場合は、カスタム実装を使用するようにコードを書き直してください。

ヒント

1〜7のアイテムを実装するMyUserRepositoryを作成します。

よりも、アイテム8を実装するMyUserManagerを作成します。

デフォルトのAspNet.Identity.EntityFrameworkクラスの代わりにそれを簡単に接続できます。

39
Anderson Matos

ALMMa が言ったことに便乗するために、私が自分のカスタム実装に取り​​組んでいたとき、この記事は非常に貴重であることがわかりました。

ASP.NET Identityのカスタムストレージプロバイダーの概要

どのインターフェイスを実装する必要があるかだけでなく、それらを実装する方法について詳しく説明し、実際のMySQL実装へのコードサンプル参照を提供します。

16
Tim Goyer

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);
        }
    }

すべてのユーザーを1つのショートで読み、メモリに保存する場合は、以下のスタイルを使用します。また、「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)

5
Narottam Goyal

既定ではEntity FrameworkとSQL Serverにバインドされていますが、SharePoint、Windows Azureストレージテーブルサービス、NoSQLデータベースなど、他のデータストアに簡単にプラグインでき、データベーススキーマの制御を維持できます。

さらに読む
ASP.NET IDの概要

4
Robert Harvey