【问题标题】:Custom ASP.NET Identity 2.0 UserStore - Is implementing all interfaces required?自定义 ASP.NET Identity 2.0 UserStore - 是否需要实现所有接口?
【发布时间】:2014-08-28 14:26:40
【问题描述】:

我为我的应用程序创建了一个自定义IUserStore<TUser,int>。我已经实现了我需要的接口,

   IUserStore<TUser, int>,
   IUserRoleStore<TUser, int>,
   IUserLockoutStore<TUser, int>,
   IUserPasswordStore<TUser, int>

但是当我打电话时

var result = await SignInManager.PasswordSignInAsync(model.UserName, model.Password, model.RememberMe, shouldLockout: false);

我得到一个例外说

Store does not implement IUserTwoFactorStore<TUser>.

我没有在我的应用程序的任何地方使用两因素身份验证。为什么它希望我实现那个接口?是否需要我实现所有这些接口,即使我实际上并没有使用它们?

【问题讨论】:

    标签: c# asp.net asp.net-identity-2


    【解决方案1】:

    其实IUserTwoFactorStore接口真的很简单,目前我的实现(我也不使用二元认证)是这样的:

     ....
     public Task<bool> GetTwoFactorEnabledAsync(User user)
     {
         return Task.FromResult(false);
     }
    
     public Task SetTwoFactorEnabledAsync(User user, bool enabled)
     {
         throw new NotImplementedException();
     }
    

    它可以工作,虽然我几分钟前才这样做,并没有彻底测试整个应用程序。

    【讨论】:

    • 是的,这会起作用,但它非常讨厌。它扼杀了liskov替换原则。我认为可以肯定地说,不检查 IUserTwoFactorStore 实现是 Identity Manager 类中的设计缺陷。
    【解决方案2】:

    我遇到了同样的问题。目前,由于 SignInManager.SignInOrTwoFactor 方法盲目地检查 GetTwoFactorAuthentication,当 UserStore 未实现 IUserTwoFactorStore 时,它​​会引发异常。

    我相信 Microsoft 的意图是必须在自定义 SignInManager 类中重写 SignInManager PasswordSignInAsync 方法。不幸的是,我找不到任何指向此的文档或示例。

    这是我为解决这个问题而实现的 SignInManager 包装类:

    public class EnhancedSignInManager<TUser, TKey> : SignInManager<TUser, TKey>
        where TUser : class, IUser<TKey>
        where TKey : IEquatable<TKey>
    {
        public EnhancedSignInManager(
            UserManager<TUser, TKey> userManager, 
            IAuthenticationManager authenticationManager)
            : base(userManager, authenticationManager)
        {
        }
    
        public override async Task SignInAsync(
            TUser user, 
            bool isPersistent, 
            bool rememberBrowser)
        {
            var userIdentity = await CreateUserIdentityAsync(user).WithCurrentCulture();
    
            // Clear any partial cookies from external or two factor partial sign ins
            AuthenticationManager.SignOut(
                DefaultAuthenticationTypes.ExternalCookie, 
                DefaultAuthenticationTypes.TwoFactorCookie);
    
            if (rememberBrowser)
            {
                var rememberBrowserIdentity = AuthenticationManager
                    .CreateTwoFactorRememberBrowserIdentity(ConvertIdToString(user.Id));
    
                AuthenticationManager.SignIn(
                    new AuthenticationProperties { IsPersistent = isPersistent }, 
                    userIdentity, 
                    rememberBrowserIdentity);
            }
            else
            {
                AuthenticationManager.SignIn(
                    new AuthenticationProperties { IsPersistent = isPersistent }, 
                    userIdentity);
            }
        }
    
        private async Task<SignInStatus> SignInOrTwoFactor(TUser user, bool isPersistent)
        {
            var id = Convert.ToString(user.Id);
    
            if (UserManager.SupportsUserTwoFactor 
                && await UserManager.GetTwoFactorEnabledAsync(user.Id)
                                    .WithCurrentCulture()
                && (await UserManager.GetValidTwoFactorProvidersAsync(user.Id)
                                     .WithCurrentCulture()).Count > 0
                    && !await AuthenticationManager.TwoFactorBrowserRememberedAsync(id)
                                                   .WithCurrentCulture())
            {
                var identity = new ClaimsIdentity(
                    DefaultAuthenticationTypes.TwoFactorCookie);
    
                identity.AddClaim(new Claim(ClaimTypes.NameIdentifier, id));
    
                AuthenticationManager.SignIn(identity);
    
                return SignInStatus.RequiresVerification;
            }
            await SignInAsync(user, isPersistent, false).WithCurrentCulture();
            return SignInStatus.Success;
        }
    
        public override async Task<SignInStatus> PasswordSignInAsync(
            string userName, 
            string password, 
            bool isPersistent, 
            bool shouldLockout)
        {
            if (UserManager == null)
            {
                return SignInStatus.Failure;
            }
    
            var user = await UserManager.FindByNameAsync(userName).WithCurrentCulture();
            if (user == null)
            {
                return SignInStatus.Failure;
            }
    
            if (UserManager.SupportsUserLockout 
                && await UserManager.IsLockedOutAsync(user.Id).WithCurrentCulture())
            {
                return SignInStatus.LockedOut;
            }
    
            if (UserManager.SupportsUserPassword 
                && await UserManager.CheckPasswordAsync(user, password)
                                    .WithCurrentCulture())
            {
                return await SignInOrTwoFactor(user, isPersistent).WithCurrentCulture();
            }
            if (shouldLockout && UserManager.SupportsUserLockout)
            {
                // If lockout is requested, increment access failed count
                // which might lock out the user
                await UserManager.AccessFailedAsync(user.Id).WithCurrentCulture();
                if (await UserManager.IsLockedOutAsync(user.Id).WithCurrentCulture())
                {
                    return SignInStatus.LockedOut;
                }
            }
            return SignInStatus.Failure;
        }
    }
    

    我希望它有所帮助。干杯

    【讨论】:

    • 谢谢,帮助我克服了当前的障碍。
    • 这不是我所需要的,但这帮助我指明了正确的道路。我必须管理员,我很恼火,我不得不花费大量时间让 Identity 与我现有的数据库一起工作。
    • 我收到此错误:“System.Threading.Tasks.Task”不包含“WithCurrentCulture”的定义。我错过了什么?看来用法是正确的。
    • 如果这符合我的要求,那么非常感谢。
    • @fcaldera 'WithCurrentCultre' 可用于命名空间内的任何对象:Microsoft.AspNet.Identity',因为项目 'Microsoft.AspNet.Identity.Core' 中的 TaskExtensions 类被标记为'内部'
    【解决方案3】:

    我有同样的问题,我不想实现IUserTwoFactorStore&lt;TUser, TKey&gt; 只是说我没有实现它。但是,如果我最终想要实现它(我预计我会这样做),我也不想回去捣乱。所以我认为未来证明(和可重用)的解决方案是:(受@gjsduarte 的回答启发)

    public class SafeUserManager<TUser, TKey> : UserManager<TUser, TKey>
    {
        public override Task<bool> GetTwoFactorEnabledAsync(TKey userId)
        {
            return Store is IUserTwoFactorStore<TUser, TKey>
                ? base.GetTwoFactorEnabledAsync(userId)
                : Task.FromResult(false);
        }
    }
    

    对其他 Get[feature]EnabledAsync(TKey userId) 方法执行相同操作可能是个好主意。

    【讨论】:

      猜你喜欢
      • 2014-06-01
      • 2015-06-25
      • 2023-03-23
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-08-14
      • 1970-01-01
      相关资源
      最近更新 更多