![](/img/trans.png)
[英]UserManager.FindAsync not working with custom implementation of UserStore
[英]UserManager.FindAsync returns null after password reset
按照官方文檔( https://github.com/rustd/AspnetIdentitySample )和NuGet程序包,在為MVC5應用程序重置密碼后,我在登錄時遇到問題。 似乎Entity Framework在此過程中並未刷新其上下文,只有在重新啟動應用程序后,我才能使用正確的憑據登錄。
據我所知,我已經完成了代碼示例所做的一切。 只有我有更多的代碼和設置(例如Unity)。
這是問題區域:
public async Task<ActionResult> Login(LoginViewModel model, string returnUrl)
{
try
{
if (ModelState.IsValid)
{
ApplicationUser user = await UserManager.FindAsync(model.UserName, model.Password);
if (user != null)
{
await this.SignInAsync(user, false);
return RedirectToLocal(returnUrl);
}
else
{
model.State = ViewModelState.Error;
model.Messages = new List<string>() { "No access buddy!" };
}
}
// If we got this far, something failed, redisplay form
return View(model);
}
catch (Exception ex)
{
throw;
}
}
private async Task SignInAsync(ApplicationUser user, bool isPersistent)
{
AuthenticationManager.SignOut(DefaultAuthenticationTypes.ExternalCookie);
ClaimsIdentity identity = await UserManager.CreateIdentityAsync(user, DefaultAuthenticationTypes.ApplicationCookie);
AuthenticationManager.SignIn(new AuthenticationProperties() { IsPersistent = isPersistent }, identity);
}
當我第一次登錄時,此部分工作正常。 但是,重置密碼后,無法使用新憑據登錄(仍然使用舊版本)。
這是我的配置:
public class ApplicationUserManager : UserManager<ApplicationUser>
{
#region Constructor
public ApplicationUserManager(IUserStore<ApplicationUser> store)
: base(store)
{
this.UserTokenProvider = new TotpSecurityStampBasedTokenProvider<ApplicationUser, string>();
}
#endregion Constructor
#region Methods
public static ApplicationUserManager Create(IdentityFactoryOptions<ApplicationUserManager> options, IOwinContext context)
{
ApplicationUserManager manager = new ApplicationUserManager(new UserStore<ApplicationUser>(context.Get<SecurityDbContext>()));
manager.UserValidator = new UserValidator<ApplicationUser>(manager)
{
AllowOnlyAlphanumericUserNames = false,
RequireUniqueEmail = true
};
// Configure validation logic for passwords
manager.PasswordValidator = new PasswordValidator
{
RequiredLength = 6,
RequireNonLetterOrDigit = true,
RequireDigit = true,
RequireLowercase = true,
RequireUppercase = true,
};
// Configure user lockout defaults
manager.UserLockoutEnabledByDefault = true;
manager.DefaultAccountLockoutTimeSpan = TimeSpan.FromMinutes(5);
manager.MaxFailedAccessAttemptsBeforeLockout = 5;
// Register two factor authentication providers. This application uses Phone and Emails as a step of receiving a code for verifying the user
// You can write your own provider and plug it in here.
manager.RegisterTwoFactorProvider("Phone Code", new PhoneNumberTokenProvider<ApplicationUser>
{
MessageFormat = "Your security code is {0}"
});
manager.RegisterTwoFactorProvider("Email Code", new EmailTokenProvider<ApplicationUser>
{
Subject = "Security Code",
BodyFormat = "Your security code is {0}"
});
manager.EmailService = new EmailService();
manager.SmsService = new SmsService();
var dataProtectionProvider = options.DataProtectionProvider;
if (dataProtectionProvider != null)
{
manager.UserTokenProvider = new DataProtectorTokenProvider<ApplicationUser>(dataProtectionProvider.Create("ASP.NET Identity"));
}
return manager;
}
#endregion Methods
}
這是我在啟動期間配置的:
// Configure the db context, user manager and signin manager to use a single instance per request
app.CreatePerOwinContext(SecurityDbContext.Create);
app.CreatePerOwinContext<ApplicationUserManager>(ApplicationUserManager.Create);
app.CreatePerOwinContext<ApplicationSignInManager>(ApplicationSignInManager.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
// Configure the sign in cookie
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
LoginPath = new PathString("/Account/Login"),
Provider = new CookieAuthenticationProvider
{
// Enables the application to validate the security stamp when the user logs in.
// This is a security feature which is used when you change a password or add an external login to your account.
OnValidateIdentity = SecurityStampValidator.OnValidateIdentity<ApplicationUserManager, ApplicationUser>(
validateInterval: TimeSpan.FromMinutes(30),
regenerateIdentity: (manager, user) => user.GenerateUserIdentityAsync(manager))
}
});
app.UseExternalSignInCookie(DefaultAuthenticationTypes.ExternalCookie);
app.SetDefaultSignInAsAuthenticationType(CookieAuthenticationDefaults.AuthenticationType);
app.UseCookieAuthentication(new CookieAuthenticationOptions { });
最終,在經過幾個屏幕之后,最終用戶可以在此處創建新密碼:
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<ActionResult> ResetPassword(ResetPasswordViewModel model)
{
if (!ModelState.IsValid)
{
return View(model);
}
ApplicationUser user = await UserManager.FindByEmailAsync(model.Email);
if (user == null)
{
// Don't reveal that the user does not exist
return RedirectToAction("ResetPasswordConfirmation", "Account");
}
IdentityResult result = await UserManager.ResetPasswordAsync(user.Id, model.Code, model.Password);
if (result.Succeeded)
{
return RedirectToAction("ResetPasswordConfirmation", "Account");
}
else
{
AddErrors(result);
return View();
}
}
這里也沒有錯誤,它將新的哈希值和安全標記存儲在數據庫中。 我在考慮一些緩存,cookie或dbContext,它們在重置密碼時不會刷新。
有人有什么想法嗎?
好的,所以我終於找到了這種奇怪行為的原因。 我有以下DbConfiguration:
public class Configuration : DbConfiguration
{
public Configuration()
{
CacheTransactionHandler transactionHandler = new CacheTransactionHandler(new InMemoryCache());
this.AddInterceptor(transactionHandler);
Loaded += (sender, args) =>
{
args.ReplaceService<DbProviderServices>((s, _) => new CachingProviderServices(s, transactionHandler));
};
}
}
注釋掉回調可以解決問題,當我用第二級緩存(由https://efcache.codeplex.com/提供)替換標准DbProviderServices時,聽起來很合理。
更新:
不必完全刪除二級緩存。 相反,通過添加緩存提供程序,我可以選擇要緩存的表(以及多長時間)。 這是更新的代碼:
public class Configuration : DbConfiguration
{
public Configuration()
{
CacheTransactionHandler transactionHandler = new CacheTransactionHandler(new InMemoryCache());
this.AddInterceptor(transactionHandler);
MyCachingPolicy cachingPolicy = new MyCachingPolicy();
Loaded += (sender, args) =>
{
args.ReplaceService<DbProviderServices>((s, _) => new CachingProviderServices(s, transactionHandler, cachingPolicy));
};
}
}
internal class MyCachingPolicy : CachingPolicy
{
#region Constructor
internal MyCachingPolicy()
{
this.NonCachableTables = new List<string>()
{
"AspNetUsers",
"Resource",
"Task",
"Appointment"
};
}
#endregion Constructor
#region Properties
private List<string> NonCachableTables { get; set; }
#endregion Properties
#region Methods
#endregion Methods
protected override bool CanBeCached(ReadOnlyCollection<EntitySetBase> affectedEntitySets, string sql, IEnumerable<KeyValuePair<string, object>> parameters)
{
return !affectedEntitySets.Select(e => e.Table ?? e.Name).Any(tableName => this.NonCachableTables.Contains(tableName));
}
protected override void GetCacheableRows(ReadOnlyCollection<EntitySetBase> affectedEntitySets, out int minCacheableRows, out int maxCacheableRows)
{
base.GetCacheableRows(affectedEntitySets, out minCacheableRows, out maxCacheableRows);
}
protected override void GetExpirationTimeout(ReadOnlyCollection<EntitySetBase> affectedEntitySets, out TimeSpan slidingExpiration, out DateTimeOffset absoluteExpiration)
{
base.GetExpirationTimeout(affectedEntitySets, out slidingExpiration, out absoluteExpiration);
}
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.