ASP.NET Đặt lại mật khẩu nhận dạng


95

Làm cách nào để lấy mật khẩu của người dùng trong hệ thống Nhận dạng ASP.NET mới? Hoặc làm thế nào tôi có thể đặt lại mà không biết hiện tại (người dùng quên mật khẩu)?

Câu trả lời:


102

Trong bản phát hành hiện tại

Giả sử bạn đã xử lý xác minh yêu cầu đặt lại mật khẩu đã quên, hãy sử dụng mã sau làm mã mẫu các bước.

ApplicationDbContext =new ApplicationDbContext()
String userId = "<YourLogicAssignsRequestedUserId>";
String newPassword = "<PasswordAsTypedByUser>";
ApplicationUser cUser = UserManager.FindById(userId);
String hashedNewPassword = UserManager.PasswordHasher.HashPassword(newPassword);
UserStore<ApplicationUser> store = new UserStore<ApplicationUser>();            
store.SetPasswordHashAsync(cUser, hashedNewPassword);

Trong AspNet Nightly Build

Khung được cập nhật để hoạt động với Token để xử lý các yêu cầu như ForgetPassword. Sau khi được phát hành, hướng dẫn mã đơn giản được mong đợi.

Cập nhật:

Bản cập nhật này chỉ để cung cấp các bước rõ ràng hơn.

ApplicationDbContext context = new ApplicationDbContext();
UserStore<ApplicationUser> store = new UserStore<ApplicationUser>(context);
UserManager<ApplicationUser> UserManager = new UserManager<ApplicationUser>(store);
String userId = User.Identity.GetUserId();//"<YourLogicAssignsRequestedUserId>";
String newPassword = "test@123"; //"<PasswordAsTypedByUser>";
String hashedNewPassword = UserManager.PasswordHasher.HashPassword(newPassword);                    
ApplicationUser cUser = await store.FindByIdAsync(userId);
await store.SetPasswordHashAsync(cUser, hashedNewPassword);
await store.UpdateAsync(cUser);

bạn có biết khi nào phiên bản 1.1 sẽ được phát hành?
graycrow

Nó vẫn đang trong giai đoạn alpha và 1.0 mới được phát hành. Vì vậy, giả sử nhiều tháng. myget.org/gallery/aspnetwebstacknightly
jd4u

11
Thật kỳ lạ là lệnh gọi phương thức store.SetPasswordHashAsync (cUser, hashedNewPassword) không hoạt động với tôi, thay vào đó tôi phải đặt thủ công cUser.PasswordHash = hashedNewPassword và sau đó gọi UserManager.UpdateAsync (người dùng);
Andy Mehalick

1
Mã không hoạt động chỉ có thể xảy ra nếu ngữ cảnh truy xuất Người dùng và ngữ cảnh cửa hàng khác nhau. Mã chỉ là một bước mẫu, không chính xác. Sẽ sớm cập nhật câu trả lời để tránh vấn đề này cho người khác.
jd4u

1
Khung 1 không cung cấp. Nhưng Framework 2-alpha có ít tính năng có thể cung cấp quy trình đơn giản để xử lý các yêu cầu đặt lại mật khẩu. aspnetidentity.codeplex.com
jd4u

137

Hoặc làm thế nào tôi có thể đặt lại mà không biết hiện tại (người dùng quên mật khẩu)?

Nếu bạn muốn thay đổi mật khẩu bằng UserManager nhưng bạn không muốn cung cấp mật khẩu hiện tại của người dùng, bạn có thể tạo mã thông báo đặt lại mật khẩu và sau đó sử dụng nó ngay lập tức.

string resetToken = await UserManager.GeneratePasswordResetTokenAsync(model.Id);
IdentityResult passwordChangeResult = await UserManager.ResetPasswordAsync(model.Id, resetToken, model.NewPassword);

8
Đây là cách tốt nhất và rõ ràng nhất để đặt mật khẩu mới. Vấn đề với câu trả lời được chấp nhận là nó bỏ qua xác thực độ phức tạp của mật khẩu bằng cách truy cập trực tiếp vào trình băm mật khẩu.
Chris

6
Fyi, Bạn có thể gặp lỗi 'Không có IUserTokenProvider được đăng ký.' nếu bạn sử dụng logic trên. Xem stackoverflow.com/questions/22629936/… .
Prasad Kanaparthi

1
Tôi cho rằng điều này chỉ hoạt động đối với Microsoft.AspNet.Identity trong phiên bản 2. Bạn không thể tìm ra phương pháp GeneratePasswordResetTokenAsync trong phiên bản 1.
romanoza

Cảm ơn bạn vì câu trả lời. Nó hoạt động như một sự quyến rũ đối với tôi.
Thomas.Benz

4
Nếu bạn nhận được Mã thông báo không hợp lệ , hãy đảm bảo rằng mã SecurityStampdành cho người dùng của bạn không bị rỗng. Điều này có thể xảy ra đối với người dùng được di chuyển từ cơ sở dữ liệu khác hoặc người dùng không được tạo thông qua UserManager.CreateAsync()phương pháp.
Alisson

70

Không được chấp nhận

Đây là câu trả lời ban đầu. Nó hoạt động, nhưng có một vấn đề. Nếu AddPasswordkhông thành công thì sao? Người dùng không có mật khẩu.

Câu trả lời ban đầu: chúng ta có thể sử dụng ba dòng mã:

UserManager<IdentityUser> userManager = 
    new UserManager<IdentityUser>(new UserStore<IdentityUser>());

userManager.RemovePassword(userId);

userManager.AddPassword(userId, newPassword);

Xem thêm: http://msdn.microsoft.com/en-us/library/dn457095(v=vs.111).aspx

Hiện được đề xuất

Có lẽ tốt hơn nên sử dụng câu trả lời mà EdwardBrey đề xuất và sau đó DanielWright sau đó đã xây dựng thêm một mẫu mã.


1
Cảm ơn chúa vì điều này, tôi nghĩ rằng tôi sẽ phải tạo một cửa hàng người dùng mới cho đến khi tôi thấy điều này!
Luke

Có cách nào để làm điều này trực tiếp trong SQL không? Tôi muốn đưa cho DBA của mình một cái mầm để gọi khi cần thay vì một tệp thực thi.
Mark Richman

@MarkRichman Đó là một câu hỏi mới. Tuy nhiên, một điều bạn có thể làm là kiểm tra T-SQL được tạo chạy trên SQL Server.
Shaun Luttin

3
Hãy chú ý khi bật tính năng này, bất cứ khi nào AddPassword không thành công (tức là mật khẩu không đủ phức tạp), người dùng sẽ bị bỏ lại mà không có mật khẩu.
Chris

1
Vâng, cách tiếp cận sạch sẽ nhất mà không bỏ qua bất kỳ quy tắc kinh doanh nào (vì khi bạn truy cập trực tiếp vào trình băm mật khẩu, không có xác thực độ phức tạp của mật khẩu) là những gì Daniel Wright đã đề xuất.
Chris

29

Trên của bạn UserManager, đầu tiên gọi GeneratePasswordResetTokenAsync . Khi người dùng đã xác minh danh tính của mình (ví dụ: bằng cách nhận mã thông báo trong email), hãy chuyển mã thông báo đến ResetPasswordAsync .


1
Đang cố gắng tìm ra lý do tại sao ResetPasswordAsync yêu cầu ID người dùng và một cách hợp lý để lấy nó từ người dùng khi họ hiển thị với mã thông báo. GeneratePasswordReset sử dụng mã thông báo có hơn 150 ký tự ... có vẻ như điều đó sẽ đủ để lưu trữ mã hóa một id người dùng nên tôi không phải tự mình triển khai mã đó. :(
pettys

Tôi cho rằng nó yêu cầu ID người dùng để nó có thể nhập mã thông báo đặt lại vào Cơ sở dữ liệu danh tính dựa trên ID người dùng đó. Nếu nó không làm điều này thì làm sao khung công tác biết được mã thông báo có hợp lệ hay không. Bạn sẽ có thể kéo ID người dùng bằng User.Identity.GetUserId () hoặc tương tự.
Ryan Buddicom

1
Yêu cầu id người dùng là một lựa chọn ngớ ngẩn về phía API, mã thông báo đã có trong cơ sở dữ liệu khi ResetPassword (không đồng bộ) được gọi và chỉ cần xác thực nó với đầu vào là đủ.
Filip

@Filip, lợi thế của ResetPasswordAsyncviệc lấy ID người dùng là nhà cung cấp danh tính chỉ cần lập chỉ mục ID người dùng chứ không cần mã thông báo. Điều này cho phép nó mở rộng quy mô tốt hơn nếu có nhiều người dùng.
Edward Brey,

1
@Edward Brey tốt, làm cách nào để bạn tìm nạp id người dùng cho lệnh gọi đặt lại?
Filip

2
string message = null;
//reset the password
var result = await IdentityManager.Passwords.ResetPasswordAsync(model.Token, model.Password);
if (result.Success)
{
    message = "The password has been reset.";
    return RedirectToAction("PasswordResetCompleted", new { message = message });
}
else
{
    AddErrors(result);
}

Đoạn mã này được lấy từ dự án AspNetIdentitySample có sẵn trên github


2

Tạo phương pháp trong UserManager<TUser, TKey>

public Task<IdentityResult> ChangePassword(int userId, string newPassword)
{
     var user = Users.FirstOrDefault(u => u.Id == userId);
     if (user == null)
          return new Task<IdentityResult>(() => IdentityResult.Failed());

     var store = Store as IUserPasswordStore<User, int>;
     return base.UpdatePassword(store, user, newPassword);
}

1

Trong trường hợp đặt lại mật khẩu, bạn nên đặt lại bằng cách gửi mã thông báo đặt lại mật khẩu đến email người dùng đã đăng ký và yêu cầu người dùng cung cấp mật khẩu mới. Nếu đã tạo một thư viện .NET dễ sử dụng trên Identity framework với các settins cấu hình mặc định. Bạn có thể tìm thấy thông tin chi tiết tại liên kết blogmã nguồn tại github.


1

Tôi nghĩ rằng hướng dẫn của Microsoft cho ASP.NET Identity là một khởi đầu tốt.

https://docs.microsoft.com/en-us/aspnet/identity/overview/features-api/account-confirmation-and-password-recovery-with-aspnet-identity

Ghi chú:

Nếu bạn không sử dụng AccountController và không thể đặt lại mật khẩu của mình, hãy sử dụng Request.GetOwinContext().GetUserManager<ApplicationUserManager>();. Nếu bạn không có cùng OwinContext, bạn cần tạo một tệp mới DataProtectorTokenProvidergiống như tệp OwinContextsử dụng. Theo mặc định, hãy nhìn vào App_Start -> IdentityConfig.cs. Nên trông như thế nào new DataProtectorTokenProvider<ApplicationUser>(dataProtectionProvider.Create("ASP.NET Identity"));.

Có thể được tạo như thế này:

Không có Owin:

[HttpGet]
[AllowAnonymous]
[Route("testReset")]
public IHttpActionResult TestReset()
{
    var db = new ApplicationDbContext();
    var manager = new ApplicationUserManager(new UserStore<ApplicationUser>(db));
    var provider = new DpapiDataProtectionProvider("SampleAppName");
    manager.UserTokenProvider = new DataProtectorTokenProvider<ApplicationUser>(
        provider.Create("SampleTokenName"));

    var email = "test@test.com";

    var user = new ApplicationUser() { UserName = email, Email = email };

    var identityUser = manager.FindByEmail(email);

    if (identityUser == null)
    {
        manager.Create(user);
        identityUser = manager.FindByEmail(email);
    }

    var token = manager.GeneratePasswordResetToken(identityUser.Id);
    return Ok(HttpUtility.UrlEncode(token));
}

[HttpGet]
[AllowAnonymous]
[Route("testReset")]
public IHttpActionResult TestReset(string token)
{
    var db = new ApplicationDbContext();
    var manager = new ApplicationUserManager(new UserStore<ApplicationUser>(db));
    var provider = new DpapiDataProtectionProvider("SampleAppName");
    manager.UserTokenProvider = new DataProtectorTokenProvider<ApplicationUser>(
        provider.Create("SampleTokenName"));
    var email = "test@test.com";
    var identityUser = manager.FindByEmail(email);
    var valid = Task.Run(() => manager.UserTokenProvider.ValidateAsync("ResetPassword", token, manager, identityUser)).Result;
    var result = manager.ResetPassword(identityUser.Id, token, "TestingTest1!");
    return Ok(result);
}

Với Owin:

[HttpGet]
[AllowAnonymous]
[Route("testResetWithOwin")]
public IHttpActionResult TestResetWithOwin()
{
    var manager = Request.GetOwinContext().GetUserManager<ApplicationUserManager>();

    var email = "test@test.com";

    var user = new ApplicationUser() { UserName = email, Email = email };

    var identityUser = manager.FindByEmail(email);

    if (identityUser == null)
    {
        manager.Create(user);
        identityUser = manager.FindByEmail(email);
    }

    var token = manager.GeneratePasswordResetToken(identityUser.Id);
    return Ok(HttpUtility.UrlEncode(token));
}

[HttpGet]
[AllowAnonymous]
[Route("testResetWithOwin")]
public IHttpActionResult TestResetWithOwin(string token)
{
    var manager = Request.GetOwinContext().GetUserManager<ApplicationUserManager>();

    var email = "test@test.com";
    var identityUser = manager.FindByEmail(email);
    var valid = Task.Run(() => manager.UserTokenProvider.ValidateAsync("ResetPassword", token, manager, identityUser)).Result;
    var result = manager.ResetPassword(identityUser.Id, token, "TestingTest1!");
    return Ok(result);
}

Các DpapiDataProtectionProviderDataProtectorTokenProvidercần phải được tạo ra với cùng tên cho một thiết lập lại mật khẩu để làm việc. Việc sử dụng Owin để tạo mã thông báo đặt lại mật khẩu và sau đó tạo mã mới DpapiDataProtectionProvidervới tên khác sẽ không hoạt động.

Mã mà tôi sử dụng cho ASP.NET Identity:

Web.Config:

<add key="AllowedHosts" value="example.com,example2.com" />

AccountController.cs:

[Route("RequestResetPasswordToken/{email}/")]
[HttpGet]
[AllowAnonymous]
public async Task<IHttpActionResult> GetResetPasswordToken([FromUri]string email)
{
    if (!ModelState.IsValid)
        return BadRequest(ModelState);

    var user = await UserManager.FindByEmailAsync(email);
    if (user == null)
    {
        Logger.Warn("Password reset token requested for non existing email");
        // Don't reveal that the user does not exist
        return NoContent();
    }

    //Prevent Host Header Attack -> Password Reset Poisoning. 
    //If the IIS has a binding to accept connections on 80/443 the host parameter can be changed.
    //See https://security.stackexchange.com/a/170759/67046
    if (!ConfigurationManager.AppSettings["AllowedHosts"].Split(',').Contains(Request.RequestUri.Host)) {
            Logger.Warn($"Non allowed host detected for password reset {Request.RequestUri.Scheme}://{Request.Headers.Host}");
            return BadRequest();
    }

    Logger.Info("Creating password reset token for user id {0}", user.Id);

    var host = $"{Request.RequestUri.Scheme}://{Request.Headers.Host}";
    var token = await UserManager.GeneratePasswordResetTokenAsync(user.Id);
    var callbackUrl = $"{host}/resetPassword/{HttpContext.Current.Server.UrlEncode(user.Email)}/{HttpContext.Current.Server.UrlEncode(token)}";

    var subject = "Client - Password reset.";
    var body = "<html><body>" +
               "<h2>Password reset</h2>" +
               $"<p>Hi {user.FullName}, <a href=\"{callbackUrl}\"> please click this link to reset your password </a></p>" +
               "</body></html>";

    var message = new IdentityMessage
    {
        Body = body,
        Destination = user.Email,
        Subject = subject
    };

    await UserManager.EmailService.SendAsync(message);

    return NoContent();
}

[HttpPost]
[Route("ResetPassword/")]
[AllowAnonymous]
public async Task<IHttpActionResult> ResetPasswordAsync(ResetPasswordRequestModel model)
{
    if (!ModelState.IsValid)
        return NoContent();

    var user = await UserManager.FindByEmailAsync(model.Email);
    if (user == null)
    {
        Logger.Warn("Reset password request for non existing email");
        return NoContent();
    }            

    if (!await UserManager.UserTokenProvider.ValidateAsync("ResetPassword", model.Token, UserManager, user))
    {
        Logger.Warn("Reset password requested with wrong token");
        return NoContent();
    }

    var result = await UserManager.ResetPasswordAsync(user.Id, model.Token, model.NewPassword);

    if (result.Succeeded)
    {
        Logger.Info("Creating password reset token for user id {0}", user.Id);

        const string subject = "Client - Password reset success.";
        var body = "<html><body>" +
                   "<h1>Your password for Client was reset</h1>" +
                   $"<p>Hi {user.FullName}!</p>" +
                   "<p>Your password for Client was reset. Please inform us if you did not request this change.</p>" +
                   "</body></html>";

        var message = new IdentityMessage
        {
            Body = body,
            Destination = user.Email,
            Subject = subject
        };

        await UserManager.EmailService.SendAsync(message);
    }

    return NoContent();
}

public class ResetPasswordRequestModel
{
    [Required]
    [Display(Name = "Token")]
    public string Token { get; set; }

    [Required]
    [Display(Name = "Email")]
    public string Email { get; set; }

    [Required]
    [StringLength(100, ErrorMessage = "The {0} must be at least {2} characters long.", MinimumLength = 10)]
    [DataType(DataType.Password)]
    [Display(Name = "New password")]
    public string NewPassword { get; set; }

    [DataType(DataType.Password)]
    [Display(Name = "Confirm new password")]
    [Compare("NewPassword", ErrorMessage = "The new password and confirmation password do not match.")]
    public string ConfirmPassword { get; set; }
}

1

Tôi đã thực hiện một cuộc điều tra nhỏ và giải pháp phù hợp với tôi là sự kết hợp của một số giải pháp được đưa ra trong bài đăng này.

Về cơ bản tôi đang biên soạn giải pháp này và tôi đang đăng những gì phù hợp với mình. Trong trường hợp của tôi, tôi không muốn sử dụng bất kỳ mã thông báo nào từ lõi .net.

public async Task ResetPassword(string userId, string password)
{
    var user = await _userManager.FindByIdAsync(userId);
    var hashPassword= _userManager.PasswordHasher.HashPassword(user, password);
    user.PasswordHash = passwordHash;
    await _userManager.UpdateAsync(user);

}

0

Cách tốt nhất để Đặt lại Mật khẩu trong Asp.Net Core Identity sử dụng cho API Web.

Lưu ý * : Error () và Result () được tạo để sử dụng nội bộ. Bạn có thể trả lại bạn muốn.

        [HttpPost]
        [Route("reset-password")]
        public async Task<IActionResult> ResetPassword(ResetPasswordModel model)
        {
            if (!ModelState.IsValid)
                return BadRequest(ModelState);
            try
            {
                if (model is null)
                    return Error("No data found!");


                var user = await _userManager.FindByIdAsync(AppCommon.ToString(GetUserId()));
                if (user == null)
                    return Error("No user found!");

                Microsoft.AspNetCore.Identity.SignInResult checkOldPassword =
                    await _signInManager.PasswordSignInAsync(user.UserName, model.OldPassword, false, false);

                if (!checkOldPassword.Succeeded)
                    return Error("Old password does not matched.");

                string resetToken = await _userManager.GeneratePasswordResetTokenAsync(user);
                if (string.IsNullOrEmpty(resetToken))
                    return Error("Error while generating reset token.");

                var result = await _userManager.ResetPasswordAsync(user, resetToken, model.Password);

                if (result.Succeeded)
                    return Result();
                else
                    return Error();
            }
            catch (Exception ex)
            {
                return Error(ex);
            }
        }
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.