Изменение пароля пользователя только с новым паролем [дубликат]

Что такое NullPointerException?

Хорошим местом для начала является JavaDocs . Они охватывают это:

Брошено, когда приложение пытается использовать null в случае, когда требуется объект. К ним относятся:

  • Вызов метода экземпляра нулевого объекта.
  • Доступ или изменение поля нулевого объекта.
  • Выполнение длины null, как если бы это был массив.
  • Доступ или изменение слотов с нулевым значением, как если бы это был массив.
  • Бросать нуль, как если бы это было значение Throwable.

Приложения должны бросать экземпляры этого класса для указания других незаконных видов использования нулевого объекта.

blockquote>

Также, если вы попытаетесь использовать нулевую ссылку с synchronized, который также выдаст это исключение, за JLS :

SynchronizedStatement:
    synchronized ( Expression ) Block
  • В противном случае, если значение выражения равно null, NullPointerException.
blockquote>

Как это исправить?

Итак, у вас есть NullPointerException. Как вы это исправите? Возьмем простой пример, который выдает NullPointerException:

public class Printer {
    private String name;

    public void setName(String name) {
        this.name = name;
    }

    public void print() {
        printString(name);
    }

    private void printString(String s) {
        System.out.println(s + " (" + s.length() + ")");
    }

    public static void main(String[] args) {
        Printer printer = new Printer();
        printer.print();
    }
}

Идентифицирует нулевые значения

. Первый шаг - точно определить , значения которого вызывают исключение . Для этого нам нужно выполнить некоторую отладку. Важно научиться читать stacktrace . Это покажет вам, где было выбрано исключение:

Exception in thread "main" java.lang.NullPointerException
    at Printer.printString(Printer.java:13)
    at Printer.print(Printer.java:9)
    at Printer.main(Printer.java:19)

Здесь мы видим, что исключение выбрано в строке 13 (в методе printString). Посмотрите на строку и проверьте, какие значения равны нулю, добавив протоколирующие операторы или используя отладчик . Мы обнаруживаем, что s имеет значение null, а вызов метода length на него вызывает исключение. Мы видим, что программа перестает бросать исключение, когда s.length() удаляется из метода.

Трассировка, где эти значения взяты из

Затем проверьте, откуда это значение. Следуя вызовам метода, мы видим, что s передается с printString(name) в методе print(), а this.name - null.

Трассировка, где эти значения должны быть установлены

Где установлен this.name? В методе setName(String). С некоторой дополнительной отладкой мы видим, что этот метод вообще не вызывается. Если этот метод был вызван, обязательно проверьте порядок , что эти методы вызывают, а метод set не будет называться после методом печати. ​​

Этого достаточно, чтобы дать нам решение: добавить вызов printer.setName() перед вызовом printer.print().

Другие исправления

Переменная может иметь значение по умолчанию setName может помешать ему установить значение null):

private String name = "";

Либо метод print, либо printString может проверить значение null например:

printString((name == null) ? "" : name);

Или вы можете создать класс, чтобы name всегда имел ненулевое значение :

public class Printer {
    private final String name;

    public Printer(String name) {
        this.name = Objects.requireNonNull(name);
    }

    public void print() {
        printString(name);
    }

    private void printString(String s) {
        System.out.println(s + " (" + s.length() + ")");
    }

    public static void main(String[] args) {
        Printer printer = new Printer("123");
        printer.print();
    }
}

См. также:

Я все еще не могу найти проблему

Если вы попытались отладить проблему и до сих пор не имеете решения, вы можете отправить вопрос для получения дополнительной справки, но не забудьте включить то, что вы пробовали до сих пор. Как минимум, включите stacktrace в вопрос и отметьте важные номера строк в коде. Также попробуйте сначала упростить код (см. SSCCE ).

33
задан Oleg Sh 27 March 2015 в 01:36
поделиться

8 ответов

ApplicationUserManager - это класс, сгенерированный шаблоном ASP.NET.

Это означает, что вы можете отредактировать его и добавить любую функциональность, которой он еще не был. Класс UserManager имеет защищенное свойство с именем Store, в котором хранится ссылка на класс UserStore (или любой его подкласс в зависимости от того, как вы настроили вашу идентификационную информацию ASP.NET или используете пользовательские реализации хранилища пользователей, то есть, если вы используйте другой механизм базы данных, такой как 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. Проверьте документацию интерфейса IUserPasswordStore в MSDN на доступных методах.

Изменить: PasswordHasher также является общедоступным свойством класса UserManager, см. определение интерфейса здесь .

Редактировать 2: Поскольку некоторые люди наивно полагать, вы не можете сделать проверку пароля таким образом, я его обновил. Свойство PasswordHasher также является свойством UserManager, а также просто добавляет 2 строки кода, чтобы добавить проверку пароля (но это не было требование первоначального вопроса).

24
ответ дан Tseng 18 August 2018 в 15:27
поделиться
  • 1
    Это интересный подход. Мне интересно, что мы можем реализовать, чтобы пароль был изменен в одной транзакции, хотя администратор не знает исходного пароля. – Shaun Luttin 27 March 2015 в 03:00
  • 2
    Этот подход является одной транзакцией. Вам не нужен старый пароль для него, так как недавно реализованная функция не использует какую-либо функцию старого и напрямую вызывает SetPasswordHashAsync из реализации UserStore, если она есть. Все, что вам нужно знать, это userId и newPassword (которые вы можете произвольно генерировать) – Tseng 27 March 2015 в 08:02

Это просто уточнение ответа, предоставленного @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 в качестве основных ключей для пользователей и ролей. Я считаю, что просто нужно было бы отключить args <AppUser, int> типа, чтобы заставить его работать с установкой Identity по умолчанию ASP.NET.

5
ответ дан BCA 18 August 2018 в 15:27
поделиться

Этот метод работал для меня:

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();
}
37
ответ дан Jaroslav Kadlec 18 August 2018 в 15:27
поделиться
  • 1
    Хотя этот код может хорошо работать, хороший ответ также должен объяснить, как он работает и почему он является хорошим решением. – Blackwood 24 December 2015 в 22:35
  • 2
    методы изменения пароля по умолчанию в классе UserManager сначала попробуйте подтвердить пароль и выполнить проверки безопасности, но я обновил значение пароля прямо как обычное поле, которое не имеет особых ограничений для него. – bryan c 24 December 2015 в 23:25
  • 3
    Что такое User credentials Model? – Dov Miller 19 January 2017 в 11:24
  • 4
    UsercredentialsModel - это простой пользовательский класс в качестве модели пользователя. в моем случае это что-то вроде этого «public class UsercredentialsModel {public string Id {get; задавать; } public string PhoneNumber {get; задавать; } public string VerificationCode {get; задавать; } public string Пароль {get; задавать; }} '@Dov Miller – bryan c 22 January 2017 в 17:35
  • 5
    хорошая работа bryan c – Ishara Samintha Ranaweera 17 February 2017 в 10:11
 public async Task<List<string>> AsyncModifyPassword(LoginDTO entity)
    {
        List<string> errors = new List<string>();
        ApplicationUser user = await _userManager.FindByEmailAsync(entity.Email);
        if (user == null)
        {
            errors.Add("User Not Found"); //todo, hablar sobre el tema de lanzar las excepciones
            return errors;
        }

        //user.PasswordHash = _userManager.PasswordHasher.HashPassword(user, entity.Password);
        IdentityResult result = await _userManager.ChangePasswordAsync(user, entity.Password , entity.NewPassword);

        if (!result.Succeeded)
            errors = result.Errors.ToList().Select(error => error.Description).ToList();

        return errors;

    }
0
ответ дан Juan David Correa 18 August 2018 в 15:27
поделиться
  • 1
    Как правило, ответы гораздо полезнее, если они включают объяснение того, что должен делать код, и почему это решает проблему, не представляя других. – Tom Aranda 10 December 2017 в 01:21

EDIT: Я знаю, что OP запросил ответ, который выполняет задачу в одной транзакции, но я думаю, что код полезен людям.

Все ответы используют PasswordHasher напрямую, что не очень хорошо поскольку вы потеряете некоторые испеченные функциональные возможности (валидация и т. д.).

Альтернативой (и я бы предположил, что рекомендуемый подход) является создание токена сброса пароля, а затем использование этого для изменения пароля. Пример:

var user = await UserManager.FindByIdAsync(id);

var token = await UserManager.GeneratePasswordResetTokenAsync(user);

var result = await UserManager.ResetPasswordAsync(user, token, "MyN3wP@ssw0rd");
27
ответ дан Lee Gunn 18 August 2018 в 15:27
поделиться
  • 1
    Это кажется правильным способом и открывает много новых возможностей. Спасибо! – peter_the_oak 28 September 2017 в 17:12
  • 2
    Мне нравится этот ответ лучше – Joe Lu 13 June 2018 в 04:35
  • 3
    Разумеется, вы также можете использовать свойство PasswordValidator, определенное в UserManager, для его проверки перед его настройкой. Но это не было требование первоначального вопроса – Tseng 16 August 2018 в 07:24
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");
            }
      }
 }
0
ответ дан Majid AGT 18 August 2018 в 15:27
поделиться
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 я извлекаю из текущего пользователя.

2
ответ дан Mansur Haider 18 August 2018 в 15:27
поделиться

Да, вы правы. ResetPassword через токен - предпочтительный подход. Когда-то назад я создал полную оболочку поверх .NET Identity, и код можно найти здесь здесь . Это может быть полезно для вас. Вы также можете найти nuget здесь . Я также объяснил библиотеку в блоге здесь . Эта оболочка легко расходуется как nuget и создает все необходимые конфигурации во время установки.

-2
ответ дан Rahul Garg 18 August 2018 в 15:27
поделиться
  • 1
    Предпочитают ответы с содержанием в них, а не внешними ссылками. Если эти ссылки когда-либо умирали по какой-либо причине, этот ответ был бы совершенно бесполезен. Кроме того, вы отвечаете на вопрос, который старше двух лет с принятым ответом два года спустя, не принося никакой дополнительной ценности. – shortstuffsushi 10 July 2017 в 04:17
Другие вопросы по тегам:

Похожие вопросы: