Microsoft.AspNetCore.Identity框架自帶的IPasswordHasher密碼加密驗證方法


本文討論的.net版本為.NET6。眾所周知,.Net Identity框架中用戶的創建修改,密碼的驗證和修改都是在UserManager中完成的。

 其中關於密碼的主要是以下幾個方法:

 主要看ChangePasswordAsync方法內部實現:

    /// <summary>
    /// Changes a user's password after confirming the specified <paramref name="currentPassword"/> is correct,
    /// as an asynchronous operation.
    /// </summary>
    /// <param name="user">The user whose password should be set.</param>
    /// <param name="currentPassword">The current password to validate before changing.</param>
    /// <param name="newPassword">The new password to set for the specified <paramref name="user"/>.</param>
    /// <returns>
    /// The <see cref="Task"/> that represents the asynchronous operation, containing the <see cref="IdentityResult"/>
    /// of the operation.
    /// </returns>
    public virtual async Task<IdentityResult> ChangePasswordAsync(TUser user, string currentPassword, string newPassword)
    {
        ThrowIfDisposed();
        var passwordStore = GetPasswordStore();
        if (user == null)
        {
            throw new ArgumentNullException(nameof(user));
        }
 
        if (await VerifyPasswordAsync(passwordStore, user, currentPassword).ConfigureAwait(false) != PasswordVerificationResult.Failed)
        {
            var result = await UpdatePasswordHash(passwordStore, user, newPassword).ConfigureAwait(false);
            if (!result.Succeeded)
            {
                return result;
            }
            return await UpdateUserAsync(user).ConfigureAwait(false);
        }
        Logger.LogDebug(LoggerEventIds.ChangePasswordFailed, "Change password failed for user.");
        return IdentityResult.Failed(ErrorDescriber.PasswordMismatch());
    }

其中 VerifyPasswordAsync(passwordStore, user, currentPassword) 方法是先驗證原來的密碼正確性,主要實現如下:

    /// <summary>
    /// Returns a <see cref="PasswordVerificationResult"/> indicating the result of a password hash comparison.
    /// </summary>
    /// <param name="store">The store containing a user's password.</param>
    /// <param name="user">The user whose password should be verified.</param>
    /// <param name="password">The password to verify.</param>
    /// <returns>
    /// The <see cref="Task"/> that represents the asynchronous operation, containing the <see cref="PasswordVerificationResult"/>
    /// of the operation.
    /// </returns>
    protected virtual async Task<PasswordVerificationResult> VerifyPasswordAsync(IUserPasswordStore<TUser> store, TUser user, string password)
    {
        var hash = await store.GetPasswordHashAsync(user, CancellationToken).ConfigureAwait(false);
        if (hash == null)
        {
            return PasswordVerificationResult.Failed;
        }
        return PasswordHasher.VerifyHashedPassword(user, hash, password);
    }

主要是 PasswordHasher.VerifyHashedPassword(user, hash, password) 的驗證方法

    /// <summary>
    /// Returns a <see cref="PasswordVerificationResult"/> indicating the result of a password hash comparison.
    /// </summary>
    /// <param name="user">The user whose password should be verified.</param>
    /// <param name="hashedPassword">The hash value for a user's stored password.</param>
    /// <param name="providedPassword">The password supplied for comparison.</param>
    /// <returns>A <see cref="PasswordVerificationResult"/> indicating the result of a password hash comparison.</returns>
    /// <remarks>Implementations of this method should be time consistent.</remarks>
    public virtual PasswordVerificationResult VerifyHashedPassword(TUser user, string hashedPassword, string providedPassword)
    {
        if (hashedPassword == null)
        {
            throw new ArgumentNullException(nameof(hashedPassword));
        }
        if (providedPassword == null)
        {
            throw new ArgumentNullException(nameof(providedPassword));
        }
 
        byte[] decodedHashedPassword = Convert.FromBase64String(hashedPassword);
 
        // read the format marker from the hashed password
        if (decodedHashedPassword.Length == 0)
        {
            return PasswordVerificationResult.Failed;
        }
        switch (decodedHashedPassword[0])
        {
            case 0x00:
                if (VerifyHashedPasswordV2(decodedHashedPassword, providedPassword))
                {
                    // This is an old password hash format - the caller needs to rehash if we're not running in an older compat mode.
                    return (_compatibilityMode == PasswordHasherCompatibilityMode.IdentityV3)
                        ? PasswordVerificationResult.SuccessRehashNeeded
                        : PasswordVerificationResult.Success;
                }
                else
                {
                    return PasswordVerificationResult.Failed;
                }
 
            case 0x01:
                int embeddedIterCount;
                if (VerifyHashedPasswordV3(decodedHashedPassword, providedPassword, out embeddedIterCount))
                {
                    // If this hasher was configured with a higher iteration count, change the entry now.
                    return (embeddedIterCount < _iterCount)
                        ? PasswordVerificationResult.SuccessRehashNeeded
                        : PasswordVerificationResult.Success;
                }
                else
                {
                    return PasswordVerificationResult.Failed;
                }
 
            default:
                return PasswordVerificationResult.Failed; // unknown format marker
        }
    }

首先會對已保存的hashPassword進行base64的解密,然后再將輸入密碼進行hash之后與base64解密的密碼進行比較,一致則成功。

注:這里雖然輸入了user對象,但是實際中並沒有用到。

 

再看 UpdatePasswordHash(passwordStore, user, newPassword) 方法中,

    /// <summary>
    /// Updates a user's password hash.
    /// </summary>
    /// <param name="user">The user.</param>
    /// <param name="newPassword">The new password.</param>
    /// <param name="validatePassword">Whether to validate the password.</param>
    /// <returns>Whether the password has was successfully updated.</returns>
    protected virtual Task<IdentityResult> UpdatePasswordHash(TUser user, string newPassword, bool validatePassword)
        => UpdatePasswordHash(GetPasswordStore(), user, newPassword, validatePassword);
 
    private async Task<IdentityResult> UpdatePasswordHash(IUserPasswordStore<TUser> passwordStore,
        TUser user, string? newPassword, bool validatePassword = true)
    {
        if (validatePassword)
        {
            var validate = await ValidatePasswordAsync(user, newPassword).ConfigureAwait(false);
            if (!validate.Succeeded)
            {
                return validate;
            }
        }
        var hash = newPassword != null ? PasswordHasher.HashPassword(user, newPassword) : null;
        await passwordStore.SetPasswordHashAsync(user, hash, CancellationToken).ConfigureAwait(false);
        await UpdateSecurityStampInternal(user).ConfigureAwait(false);
        return IdentityResult.Success;
    }

先會 ValidatePasswordAsync 驗證密碼的規則,如必須6位以上,英文數組組合等。然后再調用 PasswordHasher.HashPassword(user, newPassword) 方法加密

/// <summary>
    /// Returns a hashed representation of the supplied <paramref name="password"/> for the specified <paramref name="user"/>.
    /// </summary>
    /// <param name="user">The user whose password is to be hashed.</param>
    /// <param name="password">The password to hash.</param>
    /// <returns>A hashed representation of the supplied <paramref name="password"/> for the specified <paramref name="user"/>.</returns>
    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));
        }
    }

將密碼進行hash算法以后再做base64轉碼。這里面密碼主要算法是PBKDF2,有興趣可以自行了解(PBKDF2簡單而言就是將salted hash進行多次重復計算)。

注:這里雖然輸入了user對象,但是實際中也並沒有用到。

由此可知,這里的加密和驗證方法主要是 IPasswordHasher<TUser> 和其實現類 PasswordHasher<TUser>,而且user對象並沒有起到實際作用。如果我們在項目中要使用identity自帶的密碼驗證方法,只需注入相關依賴即可直接使用

builder.Services.AddScoped<IPasswordHasher<User>, PasswordHasher<User>>();
            var hashPassword = _passwordHasher.HashPassword(user, password);//加密
            var verfied = _passwordHasher.VerifyHashedPassword(user, user.Password, password);
            if (verfied != PasswordVerificationResult.Success)
            {
                return Result.Error("密碼錯誤,請重新輸入");
            }

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM