Пароль сброса удостоверения ASP.NET

Как я могу получить пароль пользователя в новой системе идентификации ASP.NET? Или как сделать сброс, не зная текущего (пользователь забыл пароль)?


person daniel    schedule 22.10.2013    source источник


Ответы (10)


В текущей версии

Предполагая, что вы выполнили проверку запроса на сброс забытого пароля, используйте следующий код в качестве примера шагов кода.

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);

В AspNet Nightly Build

Платформа обновлена ​​для работы с Token для обработки таких запросов, как ForgetPassword. После выпуска ожидается простое руководство по коду.

Обновление:

Это обновление предназначено только для того, чтобы предоставить более четкие шаги.

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);
person jd4u    schedule 22.10.2013
comment
вы знаете, когда выйдет версия 1.1? - person graycrow; 23.10.2013
comment
Он все еще в альфа-версии, а версия 1.0 только что выпущена. Так что предположим, что много месяцев. myget.org/gallery/aspnetwebstacknightly - person jd4u; 23.10.2013
comment
Как ни странно, вызов метода store.SetPasswordHashAsync (cUser, hashedNewPassword) у меня не сработал, вместо этого мне пришлось вручную установить cUser.PasswordHash = hashedNewPassword, а затем вызвать UserManager.UpdateAsync (user); - person Andy Mehalick; 26.10.2013
comment
Код не работает, только если контекст извлечения пользователя и контекст хранилища различны. Код был лишь примерным, а не точным. Скоро обновлю ответ, чтобы избежать этой проблемы для других. - person jd4u; 27.10.2013
comment
Превосходно. Ваш обновленный код отлично работает у меня. Единственная загвоздка в том, что это должно быть внутри асинхронного метода, и это нормально для того, что я делаю. Спасибо еще раз! - person DigiOz Multimedia; 09.11.2013
comment
Предполагая, что вы выполнили проверку запроса на сброс забытого пароля. Предоставляет ли удостоверение ASP.NET эту функцию, или нам придется писать ее с нуля. У вас есть ссылки на примеры, как это сделать? - person Teevus; 15.03.2014
comment
Framework 1 не предоставляет. Но у Framework 2-alpha есть несколько функций, которые могут обеспечить простой процесс обработки запросов на сброс пароля. aspnetidentity.codeplex.com - person jd4u; 18.03.2014
comment
Вы получили то, что я хотел. Я помещаю ваш код в результат действия с тегом [Authorize], а внизу помещаю Response.Write (пароль изменен на: + cUser.NickName...., Затем Return null. Итак, тогда, поскольку контроллер сначала ищет строку запроса , Я помещаю параметры для userId и newPassword. Конечно, мне нужно будет получить идентификатор пользователя из базы данных, но теперь у меня есть возможность изменить пароль для друга, у которого сломан компьютер (на моем веб-сайте, конечно) :::::::::: public async Task ‹ActionResult› ChangePassword (string userId, string newOne) {// здесь код jd4u - person JustJohn; 28.07.2016
comment
да, пароль не обновлялся до звонка store.UpdateAsync - person Jeremy Ray Brown; 23.02.2019

Или как сделать сброс, не зная текущего (пользователь забыл пароль)?

Если вы хотите изменить пароль с помощью UserManager, но не хотите указывать текущий пароль пользователя, вы можете сгенерировать токен сброса пароля и затем сразу же использовать его.

string resetToken = await UserManager.GeneratePasswordResetTokenAsync(model.Id);
IdentityResult passwordChangeResult = await UserManager.ResetPasswordAsync(model.Id, resetToken, model.NewPassword);
person Daniel Wright    schedule 24.03.2015
comment
Это, безусловно, лучший и самый чистый способ установить новый пароль. Проблема с принятым ответом заключается в том, что он обходит проверку сложности пароля, напрямую обращаясь к хеширующему паролю. - person Chris; 17.04.2015
comment
Fyi, вы можете получить ошибку «IUserTokenProvider не зарегистрирован». если вы воспользуетесь приведенной выше логикой. См. Этот stackoverflow.com/questions/22629936/. - person Prasad Kanaparthi; 30.08.2015
comment
Полагаю, это работает только для Microsoft.AspNet.Identity версии 2. Вы не можете найти метод GeneratePasswordResetTokenAsync в версии 1. - person romanoza; 13.01.2016
comment
Спасибо за ваш ответ. На меня это действует как оберег. - person Thomas.Benz; 16.03.2017
comment
Я не понимаю, как вы запрашиваете у пользователя новый пароль. Как можно проверить токен сброса без указания нового пароля в качестве параметра? Почему нет отдельного метода для проверки токена сброса, чтобы вы могли запросить у пользователя новый пароль в форме, чтобы они могли ввести свой новый пароль, а затем сбросить его? - person Robert Noack; 18.11.2017
comment
Если вы получили недействительный токен, убедитесь, что SecurityStamp для вашего пользователя не равен нулю. Это может произойти с пользователями, перенесенными из других баз данных, или с пользователями, которые не были созданы с помощью метода UserManager.CreateAsync(). - person Alisson; 18.07.2018
comment
@Alisson Я использую MongoDB, и я создаю пользователя по-разному, ... как я могу использовать поставщика токенов ... мне не удается сбросить пароль, и, похоже, вы единственный, кто указывает на мою проблему - person deadManN; 17.08.2019

Устарело

Это был оригинальный ответ. Это работает, но есть проблема. Что, если AddPassword выйдет из строя? Пользователь остается без пароля.

Исходный ответ: мы можем использовать три строчки кода:

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

userManager.RemovePassword(userId);

userManager.AddPassword(userId, newPassword);

См. Также: http://msdn.microsoft.com/en-us/library/dn457095(v=vs.111).aspx

Сейчас рекомендуется

Вероятно, лучше использовать ответ, который предложил Эдвард Бри, а затем Дэниел Райт позже разработал образец кода.

person Shaun Luttin    schedule 18.03.2014
comment
Слава богу, я думал, что мне придется создать новый пользовательский магазин, пока не увидел это! - person Luke; 18.12.2014
comment
Есть ли способ сделать это прямо в SQL? Я бы с удовольствием дал своему администратору баз данных sproc для вызова, когда это необходимо, вместо исполняемого файла. - person Mark Richman; 24.02.2015
comment
@MarkRichman Это новый вопрос. Однако вы можете проверить сгенерированный T-SQL, который работает на SQL Server. - person Shaun Luttin; 25.02.2015
comment
При этом будьте осторожны, всякий раз, когда AddPassword выходит из строя (т. Е. Недостаточная сложность пароля), пользователь останется без пароля. - person Chris; 15.04.2015
comment
@Chris Этот вопрос и ответы на него направлены на следующее: stackoverflow.com/questions/29291366/ Либо создайте собственный ChangePassword метод, либо используйте существующий AddPassword и продолжайте попытки до успеха. - person Shaun Luttin; 15.04.2015
comment
Что ж, самый чистый подход без обхода каких-либо бизнес-правил (поскольку при прямом доступе к хешеру паролей нет проверки сложности пароля) - это то, что предложил Дэниел Райт. - person Chris; 17.04.2015
comment
@Chris Хороший звонок. Я обновил свой ответ, чтобы порекомендовать ответ Эдварда (добавлен за 11 месяцев до ответа Дэниела). - person Shaun Luttin; 17.04.2015

На своем UserManager сначала вызовите GeneratePasswordResetTokenAsync. После того, как пользователь подтвердил свою личность (например, получив токен по электронной почте), передайте токен в ResetPasswordAsync.

person Edward Brey    schedule 15.04.2014
comment
Попытка выяснить, почему для ResetPasswordAsync требуется идентификатор пользователя, и разумный способ получить его от пользователя, когда он появляется с токеном. GeneratePasswordReset использует токен с более чем 150 символами ... кажется, что этого было бы достаточно для криптографического хранения идентификатора пользователя, поэтому мне не нужно реализовывать это самостоятельно. :( - person pettys; 17.04.2015
comment
Я предполагаю, что он запрашивает идентификатор пользователя, чтобы он мог ввести токен сброса в базу данных идентификации для этого идентификатора пользователя. Если бы этого не было, как бы фреймворк узнал, действителен ли токен? У вас должна быть возможность получить идентификатор пользователя с помощью User.Identity.GetUserId () или аналогичного. - person Ryan Buddicom; 04.05.2015
comment
Требование идентификатора пользователя - глупый выбор со стороны API, токен уже находится в базе данных, когда вызывается ResetPassword (async), и этого должно быть достаточно, чтобы просто проверить его на вход. - person Filip; 30.07.2015
comment
@Filip, преимущество ResetPasswordAsync получения идентификатора пользователя состоит в том, что провайдеру удостоверений нужно только индексировать идентификаторы пользователей, но не токены. Это позволяет лучше масштабироваться при большом количестве пользователей. - person Edward Brey; 30.07.2015
comment
@ Эдвард Брей, так что ... больше обходов = лучшая масштабируемость? - person Filip; 30.07.2015
comment
@Filip, дополнительная поездка туда и обратно? Вы получаете токен с GeneratePasswordResetTokenAsync, отправляете его пользователю по электронной почте, ждете, пока пользователь вернет его через веб-браузер, а затем вызываете ResetPasswordAsync. ResetPasswordAsync, эквивалент UPDATE Users SET Password = @newPassword WHERE UserId = @userId AND Token = @token. Я не понимаю, где нужно лишнее путешествие туда и обратно? - person Edward Brey; 30.07.2015
comment
@ Эдвард Брей, а как получить идентификатор пользователя для вызова сброса? - person Filip; 30.07.2015
comment
@Filip, насколько мне известно, они ожидают, что вы отправите идентификатор пользователя вместе с токеном в электронном письме. Это, безусловно, становится подробным, добавляя больше к уже довольно длинным URL из-за размера токена. - person Edward Brey; 30.07.2015

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);
}

Этот фрагмент кода взят из проекта AspNetIdentitySample, доступного на github < / а>

person sclarson    schedule 22.10.2013

Создать метод в 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);
}
person tmg    schedule 02.12.2015

Я думаю, что руководство Microsoft по ASP.NET Identity - хорошее начало.

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

Примечание.

Если вы не используете AccountController и не хотите сбрасывать пароль, используйте Request.GetOwinContext().GetUserManager<ApplicationUserManager>();. Если у вас нет того же OwinContext, вам нужно создать новый DataProtectorTokenProvider, подобный тому, который используется OwinContext. По умолчанию смотрите App_Start -> IdentityConfig.cs. Должно выглядеть примерно так new DataProtectorTokenProvider<ApplicationUser>(dataProtectionProvider.Create("ASP.NET Identity"));.

Могло быть создано так:

Без Овин:

[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 = "[email protected]";

    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 = "[email protected]";
    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);
}

С Овином:

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

    var email = "[email protected]";

    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 = "[email protected]";
    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);
}

Для работы сброса пароля DpapiDataProtectionProvider и DataProtectorTokenProvider необходимо создать одно и то же имя. Использование Owin для создания токена сброса пароля с последующим созданием нового DpapiDataProtectionProvider с другим именем не сработает.

Код, который я использую для идентификации ASP.NET:

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; }
}
person Ogglas    schedule 20.09.2018

Лучший способ сбросить пароль при использовании Asp.Net Core Identity для веб-API.

Примечание * : Error () и Result () создаются для внутреннего использования. Вы можете вернуться, если хотите.

        [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);
            }
        }
person Manish Vadher    schedule 02.08.2020
comment
Это сработало и с Fx v 4.5. Другое решение не сработало. По сути, это тоже было намного проще. Вам даже не нужно получать пользователя, поскольку все методы будут принимать идентификатор. Мне просто это было нужно для временного одноразового сброса в моем интерфейсе администратора, поэтому мне не нужны были все проверки ошибок. - person Steve Hiner; 25.08.2020

В случае сброса пароля рекомендуется сбросить его, отправив токен сброса пароля на электронную почту зарегистрированного пользователя и попросив пользователя предоставить новый пароль. Если вы создали легко используемую библиотеку .NET поверх платформы Identity с настройками конфигурации по умолчанию. Подробности можно найти на ссылке в блоге и исходный код на github.

person Rahul Garg    schedule 05.08.2016

Я провел небольшое расследование, и решение, которое работает для меня, было смесью нескольких решений, изложенных в этом посте.

Я в основном компилирую это решение и публикую то, что мне подходит. В моем случае я не хочу использовать токены из ядра .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);

}
person AFetter    schedule 10.04.2020
comment
то, что работает для меня, просто недостаточно для чего-то, что имеет отношение к безопасности. Я хочу использовать как можно больше готовых .NET Core. - person Heinzlmaen; 09.10.2020