簡體   English   中英

ASP .NET Core 標識 - Microsoft.AspNetCore.Identity.SignInManager:警告:用戶未能提供正確的密碼

[英]ASP .NET Core Identity - Microsoft.AspNetCore.Identity.SignInManager: Warning: User failed to provide the correct password

我有一個運行 Identity 的現有 .NET 4.x 數據庫。 我可以用它登錄就好了。 我正在將我的應用程序升級到帶有標識的 .NET Core。 我經歷了一堆問題。 我正在做以身份獲取錯誤消息:

Microsoft.AspNetCore.Identity.UserManager:警告:用戶密碼無效。 Microsoft.AspNetCore.Identity.SignInManager:警告:用戶未能提供正確的密碼。

我正在使用的代碼是:

var findUser = await signinManager.PasswordSignInAsync(userName, Password, false, false);

-- 或 -- var au = new AspNetUser() { UserName = userName, EmailConfirmed = true }; var res = await _userManager.CheckPasswordAsync(au, Password);

我得到的確切錯誤取決於我是否嘗試通過UserManagerSignInManager

我在Startup.cs文件中設置了以下內容:

    services.AddIdentity<AspNetUser, AspNetRole>().AddEntityFrameworkStores<GolfGameContext>();
    services.Configure<IdentityOptions>(options =>
    {
        options.SignIn.RequireConfirmedAccount = false;
        options.SignIn.RequireConfirmedEmail = false;
        options.SignIn.RequireConfirmedPhoneNumber = false;
    });
    services.Configure<PasswordHasherOptions>(options => options.CompatibilityMode = PasswordHasherCompatibilityMode.IdentityV2);

我認為更改密碼散列算法的設置將允許我現有的用戶 ID/密碼組合連接到數據庫。

我已經更新了我的數據庫架構。 我已將NormalizedEmailNormalizedUserName設置為電子郵件和用戶名的大寫版本。 我還直接在數據庫中將確認的電話號碼和確認的電子郵件值設置為 true。

任何有關如何正確連接的想法都值得贊賞。

也許問題在於:Asp.net Identity 和 Asp.net Core Identity 使用不同的散列算法來生成散列密碼。 即使您將 Asp.net core PasswordHasherOptions 的設置更改為 V2 版本,生成的哈希密碼仍然與 Asp.net Identity 生成的哈希密碼不同。

對於 Asp.net Identity,它使用以下代碼來散列密碼(請參閱源代碼):

    private const int PBKDF2IterCount = 1000; // default for Rfc2898DeriveBytes
    private const int PBKDF2SubkeyLength = 256/8; // 256 bits
    private const int SaltSize = 128/8; // 128 bits

    /* =======================
     * HASHED PASSWORD FORMATS
     * =======================
     * 
     * Version 0:
     * PBKDF2 with HMAC-SHA1, 128-bit salt, 256-bit subkey, 1000 iterations.
     * (See also: SDL crypto guidelines v5.1, Part III)
     * Format: { 0x00, salt, subkey }
     */

    public static string HashPassword(string password)
    {
        if (password == null)
        {
            throw new ArgumentNullException("password");
        }

        // Produce a version 0 (see comment above) text hash.
        byte[] salt;
        byte[] subkey;
        using (var deriveBytes = new Rfc2898DeriveBytes(password, SaltSize, PBKDF2IterCount))
        {
            salt = deriveBytes.Salt;
            subkey = deriveBytes.GetBytes(PBKDF2SubkeyLength);
        }

        var outputBytes = new byte[1 + SaltSize + PBKDF2SubkeyLength];
        Buffer.BlockCopy(salt, 0, outputBytes, 1, SaltSize);
        Buffer.BlockCopy(subkey, 0, outputBytes, 1 + SaltSize, PBKDF2SubkeyLength);
        return Convert.ToBase64String(outputBytes);
    }

在 Asp.net Core Identity 中,它將使用 PasswordHasher 來散列密碼(請參閱源代碼):

    public virtual string HashPassword(TUser user, string password)
    {
        if (password == null)
        {
            throw new ArgumentNullException(nameof(password));
        }

        if (_compatibilityMode == PasswordHasherCompatibilityMode.IdentityV2)
        {
            return Convert.ToBase64String(HashPasswordV2(password, _rng));
        }
        else
        {
            return Convert.ToBase64String(HashPasswordV3(password, _rng));
        }
    }

    private static byte[] HashPasswordV2(string password, RandomNumberGenerator rng)
    {
        const KeyDerivationPrf Pbkdf2Prf = KeyDerivationPrf.HMACSHA1; // default for Rfc2898DeriveBytes
        const int Pbkdf2IterCount = 1000; // default for Rfc2898DeriveBytes
        const int Pbkdf2SubkeyLength = 256 / 8; // 256 bits
        const int SaltSize = 128 / 8; // 128 bits

        // Produce a version 2 (see comment above) text hash.
        byte[] salt = new byte[SaltSize];
        rng.GetBytes(salt);
        byte[] subkey = KeyDerivation.Pbkdf2(password, salt, Pbkdf2Prf, Pbkdf2IterCount, Pbkdf2SubkeyLength);

        var outputBytes = new byte[1 + SaltSize + Pbkdf2SubkeyLength];
        outputBytes[0] = 0x00; // format marker
        Buffer.BlockCopy(salt, 0, outputBytes, 1, SaltSize);
        Buffer.BlockCopy(subkey, 0, outputBytes, 1 + SaltSize, Pbkdf2SubkeyLength);
        return outputBytes;
    }

為了解決這個問題,因為現有的用戶密碼是使用 Asp.net Identity 散列的。 登錄時,可以查詢數據庫,根據用戶名獲取HashedPassword,然后使用Asp.net Identity VerifyHashedPassword方法驗證HashedPassword。 代碼如下:

    public async Task<IActionResult> OnPostAsync(string returnUrl = null)
    {
        returnUrl = returnUrl ?? Url.Content("~/");

        if (ModelState.IsValid)
        {

            // This doesn't count login failures towards account lockout
            // To enable password failures to trigger account lockout, set lockoutOnFailure: true
            //var result = await _signInManager.PasswordSignInAsync(Input.Email, Input.Password, Input.RememberMe, lockoutOnFailure: false);

            //get the hashedpassword from the database.
            var hashedpassword = _dbContext.Users.Where(c => c.UserName == Input.Email).FirstOrDefault().PasswordHash;

            var result = VerifyHashedPassword(hashedpassword, Input.Password);

           //if success, reditect to returnUrl, else show error message.

Asp.net Identity VerifyHashedPassword 方法:

    private const int PBKDF2IterCount = 1000; // default for Rfc2898DeriveBytes
    private const int PBKDF2SubkeyLength = 256 / 8; // 256 bits
    private const int SaltSize = 128 / 8; // 128 bits

    // hashedPassword must be of the format of HashWithPassword (salt + Hash(salt+input)
    public static bool VerifyHashedPassword(string hashedPassword, string password)
    {
        if (hashedPassword == null)
        {
            return false;
        }
        if (password == null)
        {
            throw new ArgumentNullException("password");
        }

        var hashedPasswordBytes = Convert.FromBase64String(hashedPassword);

        // Verify a version 0 (see comment above) text hash.

        if (hashedPasswordBytes.Length != (1 + SaltSize + PBKDF2SubkeyLength) || hashedPasswordBytes[0] != 0x00)
        {
            // Wrong length or version header.
            return false;
        }

        var salt = new byte[SaltSize];
        Buffer.BlockCopy(hashedPasswordBytes, 1, salt, 0, SaltSize);
        var storedSubkey = new byte[PBKDF2SubkeyLength];
        Buffer.BlockCopy(hashedPasswordBytes, 1 + SaltSize, storedSubkey, 0, PBKDF2SubkeyLength);

        byte[] generatedSubkey;
        using (var deriveBytes = new Rfc2898DeriveBytes(password, salt, PBKDF2IterCount))
        {
            generatedSubkey = deriveBytes.GetBytes(PBKDF2SubkeyLength);
        }
        return ByteArraysEqual(storedSubkey, generatedSubkey);
    }

    // Compares two byte arrays for equality. The method is specifically written so that the loop is not optimized.
    [MethodImpl(MethodImplOptions.NoOptimization)]
    private static bool ByteArraysEqual(byte[] a, byte[] b)
    {
        if (ReferenceEquals(a, b))
        {
            return true;
        }

        if (a == null || b == null || a.Length != b.Length)
        {
            return false;
        }

        var areSame = true;
        for (var i = 0; i < a.Length; i++)
        {
            areSame &= (a[i] == b[i]);
        }
        return areSame;
    }

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

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