Хорошим местом для начала является JavaDocs . Они охватывают это:
Брошено, когда приложение пытается использовать null в случае, когда требуется объект. К ним относятся:
- Вызов метода экземпляра нулевого объекта.
- Доступ или изменение поля нулевого объекта.
- Выполнение длины null, как если бы это был массив.
- Доступ или изменение слотов с нулевым значением, как если бы это был массив.
- Бросать нуль, как если бы это было значение Throwable.
Приложения должны бросать экземпляры этого класса для указания других незаконных видов использования нулевого объекта.
blockquote>Также, если вы попытаетесь использовать нулевую ссылку с
synchronized
, который также выдаст это исключение, за JLS :SynchronizedStatement: synchronized ( Expression ) Block
blockquote>
- В противном случае, если значение выражения равно null,
NullPointerException
.Как это исправить?
Итак, у вас есть
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 = "";
Либо метод
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 ).
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 строки кода, чтобы добавить проверку пароля (но это не было требование первоначального вопроса).
Это просто уточнение ответа, предоставленного @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.
Этот метод работал для меня:
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();
}
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;
}
EDIT: Я знаю, что OP запросил ответ, который выполняет задачу в одной транзакции, но я думаю, что код полезен людям.
Все ответы используют PasswordHasher напрямую, что не очень хорошо поскольку вы потеряете некоторые испеченные функциональные возможности (валидация и т. д.).
Альтернативой (и я бы предположил, что рекомендуемый подход) является создание токена сброса пароля, а затем использование этого для изменения пароля. Пример:
var user = await UserManager.FindByIdAsync(id);
var token = await UserManager.GeneratePasswordResetTokenAsync(user);
var result = await UserManager.ResetPasswordAsync(user, token, "MyN3wP@ssw0rd");
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");
}
}
}
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 я извлекаю из текущего пользователя.
Да, вы правы. ResetPassword через токен - предпочтительный подход. Когда-то назад я создал полную оболочку поверх .NET Identity, и код можно найти здесь здесь . Это может быть полезно для вас. Вы также можете найти nuget здесь . Я также объяснил библиотеку в блоге здесь . Эта оболочка легко расходуется как nuget и создает все необходимые конфигурации во время установки.
SetPasswordHashAsync
из реализацииUserStore
, если она есть. Все, что вам нужно знать, этоuserId
иnewPassword
(которые вы можете произвольно генерировать) – Tseng 27 March 2015 в 08:02