簡體   English   中英

ASP.NET 身份更改密碼

[英]ASP.NET Identity change password

我需要能夠由管理員更改用戶密碼。 所以,admin 不應該輸入用戶的當前密碼,他應該有能力設置一個新密碼。 我查看了 ChangePasswordAsync 方法,但此方法需要輸入舊密碼。 因此,此方法不適用於此任務。 因此,我通過以下方式做到了:

    [HttpPost]
    public async Task<ActionResult> ChangePassword(ViewModels.Admin.ChangePasswordViewModel model)
    {
        var userManager = HttpContext.GetOwinContext().GetUserManager<ApplicationUserManager>();
        var result = await userManager.RemovePasswordAsync(model.UserId);
        if (result.Succeeded)
        {
            result = await userManager.AddPasswordAsync(model.UserId, model.Password);
            if (result.Succeeded)
            {
                return RedirectToAction("UserList");
            }
            else
            {
                ModelState.AddModelError("", result.Errors.FirstOrDefault());
            }
        }
        else
        {
            ModelState.AddModelError("", result.Errors.FirstOrDefault());
        }
        return View(model);
    }

它有效,但理論上我們可以在 AddPasswordAsync 方法上收到錯誤。 因此,舊密碼將被刪除,但不會設置新密碼。 這樣不好。 有沒有辦法在“一次交易”中做到這一點? 附注。 我看到了帶有重置令牌的 ResetPasswordAsync 方法,似乎它更安全(因為用戶不會出現不穩定的情況),但無論如何,它通過 2 個操作來完成。

編輯:我知道 OP 要求在一個事務中執行任務的答案,但我認為該代碼對人們有用。

所有答案都直接使用 PasswordHasher 這不是一個好主意,因為您將失去一些烘焙功能(驗證等)。

另一種方法(我假設推薦的方法)是創建一個密碼重置令牌,然后使用它來更改密碼。 例子:

var user = await UserManager.FindByIdAsync(id);

var token = await UserManager.GeneratePasswordResetTokenAsync(user);

var result = await UserManager.ResetPasswordAsync(user, token, "MyN3wP@ssw0rd");

這種方法對我有用:

public async Task<IHttpActionResult> changePassword(UsercredentialsModel usermodel)
{
  ApplicationUser user = await AppUserManager.FindByIdAsync(usermodel.Id);
  if (user == null)
  {
    return NotFound();
  }
  user.PasswordHash = AppUserManager.PasswordHasher.HashPassword(usermodel.Password);
  var result = await AppUserManager.UpdateAsync(user);
  if (!result.Succeeded)
  {
    //throw exception......
  }
  return Ok();
}

ApplicationUserManager是由 ASP.NET 模板生成的類。

這意味着,您可以編輯它並添加它尚不具備的任何功能。 UserManager 類有一個名為Store的受保護屬性,它存儲對UserStore類(或其任何子類,取決於您如何配置 ASP.NET Identity 或是否使用自定義用戶存儲實現,即,如果您使用不同的數據庫引擎)的引用像 MySQL)。

public class AplicationUserManager : UserManager<....> 
{
    public async Task<IdentityResult> ChangePasswordAsync(TKey userId, string newPassword) 
    {
        var store = this.Store as IUserPasswordStore;
        if(store==null) 
        {
            var errors = new string[] 
            { 
                "Current UserStore doesn't implement IUserPasswordStore"
            };

            return Task.FromResult<IdentityResult>(new IdentityResult(errors) { Succeeded = false });
        }

        if(PasswordValidator != null)
        {
            var passwordResult = await PasswordValidator.ValidateAsync(password);
            if(!password.Result.Success)
                return passwordResult;
        }

        var newPasswordHash = this.PasswordHasher.HashPassword(newPassword);

        await store.SetPasswordHashAsync(userId, newPasswordHash);
        return Task.FromResult<IdentityResult>(IdentityResult.Success);
    }
}

UserManager只不過是底層UserStore的包裝器。 MSDN上查看有關可用方法的IUserPasswordStore接口文檔。

編輯: PasswordHasher也是UserManager類的公共屬性,請參閱此處的接口定義

編輯2:由於有些人天真地相信,您不能以這種方式進行密碼驗證,因此我對其進行了更新。 PasswordValidator屬性也是UserManager一個屬性,它就像添加 2 行代碼來添加密碼驗證一樣簡單(盡管這不是原始問題的要求)。

在 .net 核心 3.0 中

var token = await UserManager.GeneratePasswordResetTokenAsync(user);
var result = await UserManager.ResetPasswordAsync(user, token, password);

這只是對@Tseng 提供的答案的改進。 (我必須對其進行調整才能使其正常工作)。

public class AppUserManager : UserManager<AppUser, int>
{
    .
    // standard methods...
    .

    public async Task<IdentityResult> ChangePasswordAsync(AppUser user, string newPassword)
    {
        if (user == null)
            throw new ArgumentNullException(nameof(user));

        var store = this.Store as IUserPasswordStore<AppUser, int>;
        if (store == null)
        {
            var errors = new string[] { "Current UserStore doesn't implement IUserPasswordStore" };
            return IdentityResult.Failed(errors);
        }

        var newPasswordHash = this.PasswordHasher.HashPassword(newPassword);
        await store.SetPasswordHashAsync(user, newPasswordHash);
        await store.UpdateAsync(user);
        return IdentityResult.Success;
    }
}

注意:這特別適用於使用int作為用戶和角色主鍵的修改設置。 我相信這只是刪除<AppUser, int>類型參數以使其與默認 ASP.NET Identity 設置一起工作的問題。

public async Task<IActionResult> ChangePassword(ChangePwdViewModel usermodel)
        {           
            var userId = User.FindFirstValue(ClaimTypes.NameIdentifier);
            var user = await _userManager.FindByIdAsync(userId);            
            var result = await _userManager.ChangePasswordAsync(user, usermodel.oldPassword, usermodel.newPassword);
            if (!result.Succeeded)
            {
                //throw exception......
            }
            return Ok();
        }

public class ChangePwdViewModel
    {  
        [DataType(DataType.Password), Required(ErrorMessage ="Old Password Required")]
        public string oldPassword { get; set; }

        [DataType(DataType.Password), Required(ErrorMessage ="New Password Required")]
        public string newPassword { get; set; }
    }

注意:這里 UserId 我正在從當前登錄的用戶中檢索。

如果您沒有用戶的當前密碼,但仍想更改密碼。 您可以先刪除用戶的密碼,然后添加新密碼。 這樣您就可以更改用戶的密碼,而無需該用戶的當前密碼。

await UserManager.RemovePasswordAsync(user);
await UserManager.AddPasswordAsync(user, model.Password);

對於ASP.NET Core 3.1用戶,這是@Tseng 和@BCA 提供的優秀答案的現代化迭代。

PasswordValidator不再是UserManager上的屬性 - 相反,該屬性是IList PasswordValidators 此外,Identity 現在有一個protected UpdatePasswordHash方法,它可以為您更改密碼,而無需直接訪問UserStore ,這消除了手動散列和保存密碼的需要。

UserManager還有一個公共屬性bool SupportsUserPassword ,它取代了測試Store實現IUserPasswordStore的需要(在內部,這正是UserManagerSupportsUserPassword getter 中所做的)。

由於UpdatePasswordHash protected ,您仍然需要擴展基本UserManager 它的簽名是:

protected Task<IdentityResult> UpdatePasswordHash(TUser user, string newPassword, bool validatePassword)

其中validatePassword表示是否運行密碼驗證。 並不默認為true ,不幸的是,所以它需要供給。 實現如下所示:

public async Task<IdentityResult> ChangePasswordAsync(ApplicationUser user, string newPassword)
{
    if (!SupportsUserPassword)
    {
        return IdentityResult.Failed(new IdentityError
        {
            Description = "Current UserStore doesn't implement IUserPasswordStore"
        });
    }
            
    var result = await UpdatePasswordHash(user, newPassword, true);
    if (result.Succeeded)
        await UpdateAsync(user);

    return result;
}

和以前一樣,首要任務是確保當前的UserStore支持密碼。

然后,只需使用ApplicationUser 、新密碼和true調用UpdatePasswordHash以通過驗證更新該用戶的密碼。 如果更新成功,您仍然需要保存用戶,因此調用UpdateAsync

我認為解決方案要容易得多

  1. 生成密碼令牌,
  2. 使用生成的令牌重置密碼...

 public async Task<IdentityResult> ResetPasswordAsync(ApplicationUser user, string password) { string token = await userManager.GeneratePasswordResetTokenAsync(user); return await userManager.ResetPasswordAsync(user, token, password); }

public async Task<ActionResult> ChangePassword(ResetPasswordViewModel CP)
{
     ApplicationDbContext context = new ApplicationDbContext();
     UserStore<ApplicationUser> store = new UserStore<ApplicationUser>(context);
     UserManager<ApplicationUser> UserManager = new UserManager<ApplicationUser>(store);
     var user = await UserManager.FindAsync(User.Identity.Name, CP.CurrentPassword);

     if (!UserManager.CheckPassword(user, CP.CurrentPassword))
     {
           ViewBag.notification = "Incorrect password.";
           return View("~/Views/User/settings.cshtml");
     }
     else
     {
           if (CP.Password != CP.ConfirmPassword)
           {
                 ViewBag.notification = "try again";
                 return View("~/Views/User/settings.cshtml");
           }
           else
           {
                 String hashedNewPassword = UserManager.PasswordHasher.HashPassword(CP.Password);
                 await store.SetPasswordHashAsync(user, hashedNewPassword);
                 await store.UpdateAsync(user);
                 ViewBag.notification = "successful";
                 return View("~/Views/User/settings.cshtml");
            }
      }
 }
public async Task<ActionResult> ResetUserPassword(string id, string Password)
{
    //     Find User
    var user = await context.Users.Where(x => x.Id == id).SingleOrDefaultAsync();
    if (user == null)
    {
        return RedirectToAction("UserList");
    }
    await UserManager.RemovePasswordAsync(id);
    //     Add a user password only if one does not already exist
    await UserManager.AddPasswordAsync(id, Password);
    return RedirectToAction("UserDetail", new { id = id });
}

是的,你是對的。 通過令牌重置密碼是首選方法。 前段時間,我在 .NET Identity 上創建了一個完整的包裝器,可以在此處找到代碼。 它可能對你有幫助。 您還可以在此處找到 nuget。 我還在此處的博客中解釋了該庫。 這個包裝器很容易作為 nuget 使用,並在安裝過程中創建所有必需的配置。

暫無
暫無

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

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