簡體   English   中英

密碼重置后,UserManager.FindAsync返回null

[英]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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM