[英]Unable to resolve service for type 'Microsoft.AspNetCore.Identity.SignInManager` while attempting to activate 'xxxxx.LoginModel'
[英]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);
我得到的確切錯誤取決於我是否嘗試通過UserManager
或SignInManager
。
我在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/密碼組合連接到數據庫。
我已經更新了我的數據庫架構。 我已將NormalizedEmail
和NormalizedUserName
設置為電子郵件和用戶名的大寫版本。 我還直接在數據庫中將確認的電話號碼和確認的電子郵件值設置為 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.