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("密码错误,请重新输入"); }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· AI 智能体引爆开源社区「GitHub 热点速览」
· 三行代码完成国际化适配,妙~啊~
· .NET Core 中如何实现缓存的预热?