ASP.NET Kimlik sıfırlama parolası


100

Yeni ASP.NET Identity sisteminde bir kullanıcının parolasını nasıl alabilirim? Veya mevcut olanı bilmeden nasıl sıfırlayabilirim (kullanıcı şifreyi unuttu)?

Yanıtlar:


105

Mevcut sürümde

Unutulan şifreyi sıfırlama talebinin doğrulanmasını gerçekleştirdiğinizi varsayarak, aşağıdaki kodu örnek kod adımları olarak kullanın.

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'ta

Çerçeve, ForgetPassword gibi istekleri işlemek için Token ile çalışacak şekilde güncellenmiştir. Yayınlandıktan sonra, basit bir kod kılavuzu beklenmektedir.

Güncelleme:

Bu güncelleme sadece daha net adımlar sağlamak içindir.

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

1.1 sürümünün ne zaman çıkacağını biliyor musunuz?
graycrow

Hala alfa sürümünde ve 1.0 piyasaya çıktı. Öyleyse aylarca varsayalım. myget.org/gallery/aspnetwebstacknightly
jd4u

11
Garip bir şekilde store.SetPasswordHashAsync (cUser, hashedNewPassword) yöntem çağrısı benim için işe yaramadı, bunun yerine cUser.PasswordHash = hashedNewPassword öğesini manuel olarak ayarlamam ve ardından UserManager.UpdateAsync (kullanıcı) 'yı çağırmam gerekti;
Andy Mehalick

1
Kod çalışmıyor, yalnızca Kullanıcı geri alma bağlamı ve mağaza bağlamı farklıysa mümkündür. Kod yalnızca örnek bir adımdı, doğru değildi. Başkaları için bu sorunu önlemek için yanıtı yakında güncelleyecektir.
jd4u

1
Çerçeve 1 sağlamaz. Ancak Framework 2-alpha, parola sıfırlama isteklerinin işlenmesi için basit bir işlem sağlayabilen birkaç özelliğe sahiptir. aspnetidentity.codeplex.com
jd4u

143

Veya mevcut olanı bilmeden nasıl sıfırlayabilirim (kullanıcı şifreyi unuttu)?

UserManager'ı kullanarak bir parolayı değiştirmek istiyorsanız, ancak kullanıcının geçerli parolasını sağlamak istemiyorsanız, bir parola sıfırlama belirteci oluşturabilir ve bunun yerine hemen kullanabilirsiniz.

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

8
Bu, yeni bir şifre belirlemenin açık ara en iyi ve en temiz yoludur. Kabul edilen yanıtla ilgili sorun, doğrudan parola karma sistemine erişerek parola karmaşıklığı doğrulamalarını atlamasıdır.
Chris

6
Fyi, 'No IUserTokenProvider kayıtlı değil.' Hatasını alabilirsiniz. Yukarıdaki mantığı kullanırsanız. Bu stackoverflow.com/questions/22629936/… bakın .
Prasad Kanaparthi

1
Bu yalnızca sürüm 2'de Microsoft.AspNet.Identity için çalışıyor, sanırım. GeneratePasswordResetTokenAsync yöntemini sürüm 1'de
bulamıyorsunuz

Cevabınız için teşekkür ederim. Benim için bir cazibe gibi çalışıyor.
Thomas.Benz

4
Geçersiz Belirteç alırsanız SecurityStamp, kullanıcınız için boş olmadığından emin olun . Bu, diğer veritabanlarından taşınan kullanıcılar veya UserManager.CreateAsync()yöntemle oluşturulmamış kullanıcılar için olabilir .
Alisson

70

Kullanımdan kaldırıldı

Orijinal cevap buydu. Çalışıyor ama bir sorunu var. FarzedelimAddPassword başarısız olursa? Kullanıcı şifresiz bırakılır.

Orijinal cevap: Üç satır kod kullanabiliriz:

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

userManager.RemovePassword(userId);

userManager.AddPassword(userId, newPassword);

Ayrıca bakınız: http://msdn.microsoft.com/en-us/library/dn457095(v=vs.111).aspx

Şimdi Önerilen

EdwardBrey'in önerdiği cevabı kullanmak muhtemelen daha iyidir ve daha sonra DanielWright daha sonra bir kod örneği ile detaylandırmıştır .


1
Bunun için Tanrıya şükür, bunu görene kadar yeni bir kullanıcı mağazası oluşturmam gerektiğini düşündüm!
Luke

Bunu doğrudan SQL'de yapmanın bir yolu var mı? DBA'ma gerektiğinde yürütülebilir bir dosya yerine çağırması için bir sproc vermeyi çok isterim.
Mark Richman

@MarkRichman Bu yeni bir soru. Yine de yapabileceğiniz bir şey, SQL Server üzerinde çalışan oluşturulan T-SQL'i incelemektir.
Shaun Luttin

3
Buna dikkat edin, AddPassword başarısız olduğunda (yani, yetersiz parola karmaşıklığı), kullanıcı parolasız kalacaktır.
Chris

1
Daniel Wright'ın önerdiği, herhangi bir iş kuralını atlamadan en temiz yaklaşım (çünkü parola ayırıcısına doğrudan eriştiğinizde parola karmaşıklığı doğrulaması yoktur).
Chris

29

Senin üzerinde UserManager, ilk çağrı GeneratePasswordResetTokenAsync . Kullanıcı kimliğini doğruladıktan sonra (örneğin belirteci bir e-postada alarak), belirteci ResetPasswordAsync'e iletin .


2
ResetPasswordAsync'in neden bir kullanıcı kimliği gerektirdiğini ve bir belirteçle birlikte göründüklerinde kullanıcıdan bunu almanın makul bir yolunu bulmaya çalışmak. GeneratePasswordReset, 150 karakterden fazla bir token kullanıyor ... bu, bir kullanıcı kimliğini kriptografik olarak saklamak için yeterli olacak gibi görünüyor, böylece bunu kendim uygulamak zorunda kalmayayım. :(
küçükler

Kullanıcı kimliğini sorduğunu varsayıyorum, böylece sıfırlama jetonunu bu kullanıcı kimliğine karşı Kimlik Veritabanına girebilir. Bunu yapmasaydı, çerçeve belirtecin geçerli olup olmadığını nasıl bilecekti. User.Identity.GetUserId () veya benzerini kullanarak kullanıcı kimliğini çekebilmelisiniz.
Ryan Buddicom

1
Kullanıcı kimliğinin zorunlu kılınması API açısından aptalca bir seçimdir, belirteç ResetPassword (async) çağrıldığında zaten veritabanında bulunur ve girişe göre doğrulamak için yeterli olmalıdır.
Filip

@Filip, ResetPasswordAsyncbir kullanıcı kimliği almanın avantajı , kimlik sağlayıcının jetonları değil, yalnızca kullanıcı kimliklerini endekslemesi gerektiğidir. Bu, çok sayıda kullanıcı varsa daha iyi ölçeklenmesini sağlar.
Edward Brey

2
@Edward Brey iyi, sıfırlama çağrısı için kullanıcı kimliğini nasıl alırsınız?
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);
}

Bu kod parçası, github'da bulunan AspNetIdentitySample projesinden çıkarılmıştır .


2

İçinde yöntem oluştur 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);
}

2

Web API için Asp.Net Core Identity'de Parola Sıfırlamanın en iyi yolu.

Not * : Hata () ve Sonuç () dahili kullanım için oluşturulmuştur. İstediğin zaman geri dönebilirsin.

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

2
Bu benim için Fx v 4.5 ile de çalıştı. Diğer çözüm işe yaramadı. Temelde bu da çok daha basitti. Tüm yöntemler kimliği kabul edeceğinden, kullanıcıyı gerçekten elde etmenize bile gerek yok. Yönetici arayüzümde geçici bir defalık sıfırlama için ihtiyacım vardı, bu yüzden tüm hata kontrollerine ihtiyacım yoktu.
Steve Hiner

1

Şifre sıfırlama durumunda, kayıtlı kullanıcı e-postasına şifre sıfırlama belirteci gönderilerek sıfırlanması ve kullanıcıdan yeni şifre girmesini istemeniz önerilir. Varsayılan yapılandırma ayarları ile Identity çerçevesi üzerinden kolayca kullanılabilen bir .NET kitaplığı oluşturduysanız. Ayrıntıları blog bağlantısında ve kaynak kodunu github'da bulabilirsiniz.


1

ASP.NET Identity için Microsoft kılavuzunun iyi bir başlangıç ​​olduğunu düşünüyorum.

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

Not:

AccountController kullanmıyorsanız ve şifrenizi sıfırlamak istemiyorsanız, kullanın Request.GetOwinContext().GetUserManager<ApplicationUserManager>();. Aynı OwinContext'e sahip değilseniz DataProtectorTokenProvider, OwinContextkullandığı gibi yeni bir tane oluşturmanız gerekir . Varsayılan olarak bakın App_Start -> IdentityConfig.cs. Bir şeye benzemelinew DataProtectorTokenProvider<ApplicationUser>(dataProtectionProvider.Create("ASP.NET Identity")); .

Şu şekilde oluşturulabilir:

Owin olmadan:

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

Owin ile:

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

DpapiDataProtectionProviderVe DataProtectorTokenProviderihtiyaçlar çalışmalarına bir şifre sıfırlama için aynı isimde oluşturulacak. Parola sıfırlama belirteci oluşturmak için Owin'i kullanma ve ardından yeni birDpapiDataProtectionProvider başka bir adla işe yaramaz.

ASP.NET Identity için kullandığım kod:

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

Biraz araştırma yaptım ve benim için işe yarayan çözüm, bu yazıda kurulan birkaç çözümün bir karışımıydı.

Temelde bu çözümü derliyorum ve benim için neyin işe yaradığını yayınlıyorum. Benim durumumda, .net çekirdeğinden herhangi bir belirteç kullanmak istemiyorum.

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

}

"Benim için işe yarayan" güvenlikle ilgili bir şey için yeterince iyi değil. Mümkün olduğunca çok önceden hazırlanmış .NET Core kullanmak istiyorum.
Heinzlmaen
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.