Пользовательский ASP.NET Identity 2.0 UserStore. Выполняет ли все необходимые интерфейсы?
Я создал пользовательский 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>.
Я не использую двухфакторную аутентификацию в любом месте приложения. Почему он ожидает, что я реализую этот интерфейс? Требуется ли я реализовать все эти интерфейсы, даже если я их фактически не использую?
Ответы
Ответ 1
На самом деле интерфейс IUserTwoFactorStore
очень прост, до сих пор моя реализация (я тоже не использую два фактора auth):
....
public Task<bool> GetTwoFactorEnabledAsync(User user)
{
return Task.FromResult(false);
}
public Task SetTwoFactorEnabledAsync(User user, bool enabled)
{
throw new NotImplementedException();
}
Это работает, хотя я только что сделал пару минут назад и не полностью тестировал все приложение.
Ответ 2
У меня была та же проблема. На данный момент, поскольку метод SignInManager.SignInOrTwoFactor слепо проверяет GetTwoFactorAuthentication, он генерирует исключение, когда UserStore не реализует IUserTwoFactorStore.
Я считаю, что Microsoft предполагала, что метод SignInManager PasswordSignInAsync должен быть переопределен в пользовательском классе SignInManager. К сожалению, я не мог найти документацию или образцы, указывающие на это.
Ниже приведен класс обертки 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;
}
}
Надеюсь, это поможет. Приветствия
Ответ 3
У меня была такая же проблема, и я не хочу реализовывать IUserTwoFactorStore<TUser, TKey>
, просто чтобы сказать, что я ее не реализую. Но я также не хочу возвращаться и гадать, если я в конечном итоге хочу его реализовать (чего я и ожидаю). Итак, то, что я считаю будущим доказательством (и многоразовым) решением, будет: (вдохновлено ответом @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)
.