89 password reset
This commit is contained in:
parent
56f083e757
commit
90799356ab
@ -21,4 +21,5 @@ public class Constants {
|
|||||||
public static final String PASSWORD_RESET_REQUEST_PAGE = "/resetRequest";
|
public static final String PASSWORD_RESET_REQUEST_PAGE = "/resetRequest";
|
||||||
public static final String PASSWORD_RESET_PAGE = "/reset";
|
public static final String PASSWORD_RESET_PAGE = "/reset";
|
||||||
|
|
||||||
|
public static final int RESET_KEY_LENGTH = 6;
|
||||||
}
|
}
|
@ -104,7 +104,8 @@ public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
|
|||||||
.antMatchers("/css/**")
|
.antMatchers("/css/**")
|
||||||
.antMatchers("/js/**")
|
.antMatchers("/js/**")
|
||||||
.antMatchers("/templates/**")
|
.antMatchers("/templates/**")
|
||||||
.antMatchers("/webjars/**");
|
.antMatchers("/webjars/**")
|
||||||
|
.antMatchers("/img/**");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
|
@ -34,11 +34,11 @@ public class AdviceController {
|
|||||||
public AdviceController(UserService userService) {
|
public AdviceController(UserService userService) {
|
||||||
this.userService = userService;
|
this.userService = userService;
|
||||||
}
|
}
|
||||||
|
//
|
||||||
@ModelAttribute("currentUser")
|
// @ModelAttribute("currentUser")
|
||||||
public String getCurrentUser() {
|
// public String getCurrentUser() {
|
||||||
return userService.getCurrentUser().getUserAbbreviate();
|
// return userService.getCurrentUser().getUserAbbreviate();
|
||||||
}
|
// }
|
||||||
|
|
||||||
@ModelAttribute("flashMessage")
|
@ModelAttribute("flashMessage")
|
||||||
public String getFlashMessage() {
|
public String getFlashMessage() {
|
||||||
|
@ -9,9 +9,9 @@ public enum ErrorConstants {
|
|||||||
USER_EMAIL_EXISTS(102, "Пользователь с таким почтовым ящиком уже существует"),
|
USER_EMAIL_EXISTS(102, "Пользователь с таким почтовым ящиком уже существует"),
|
||||||
USER_LOGIN_EXISTS(103, "Пользователь с таким логином уже существует"),
|
USER_LOGIN_EXISTS(103, "Пользователь с таким логином уже существует"),
|
||||||
USER_PASSWORDS_NOT_VALID_OR_NOT_MATCH(104, "Пароли введены неверно"),
|
USER_PASSWORDS_NOT_VALID_OR_NOT_MATCH(104, "Пароли введены неверно"),
|
||||||
USER_NOT_FOUND(105, "User is not found"),
|
USER_NOT_FOUND(105, "Аккаунт не найден"),
|
||||||
USER_NOT_ACTIVATED(106, "User is not activated"),
|
USER_NOT_ACTIVATED(106, "User is not activated"),
|
||||||
USER_RESET_ERROR(107, "Invalid reset key"),
|
USER_RESET_ERROR(107, "Некорректный ключ подтверждения"),
|
||||||
USER_UNDEAD_ERROR(108, "Can't edit/delete that user"),
|
USER_UNDEAD_ERROR(108, "Can't edit/delete that user"),
|
||||||
FILE_UPLOAD_ERROR(110, "File upload error"),
|
FILE_UPLOAD_ERROR(110, "File upload error"),
|
||||||
USER_SENDING_MAIL_EXCEPTION(111, "Во время отправки приглашения пользователю произошла ошибка");
|
USER_SENDING_MAIL_EXCEPTION(111, "Во время отправки приглашения пользователю произошла ошибка");
|
||||||
|
@ -148,16 +148,15 @@ public class UserController extends OdinController<UserListDto, UserDto> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@PostMapping(PASSWORD_RESET_REQUEST_URL)
|
@PostMapping(PASSWORD_RESET_REQUEST_URL)
|
||||||
public Response<Boolean> requestPasswordReset(@RequestParam("email") String email) {
|
public void requestPasswordReset(@RequestParam("email") String email) {
|
||||||
log.debug("REST: UserController.requestPasswordReset( {} )", email);
|
log.debug("REST: UserController.requestPasswordReset( {} )", email);
|
||||||
return new Response<>(userService.requestUserPasswordReset(email));
|
userService.requestUserPasswordReset(email);
|
||||||
}
|
}
|
||||||
|
|
||||||
@PostMapping(PASSWORD_RESET_URL)
|
@PostMapping(PASSWORD_RESET_URL)
|
||||||
public Response<Boolean> finishPasswordReset(@RequestParam("key") String key,
|
public Response<Boolean> finishPasswordReset(@RequestBody UserResetPasswordDto userResetPasswordDto) {
|
||||||
@RequestBody UserResetPasswordDto userResetPasswordDto) {
|
log.debug("REST: UserController.requestPasswordReset( {} )", userResetPasswordDto.getResetKey());
|
||||||
log.debug("REST: UserController.requestPasswordReset( {} )", key);
|
return new Response<>(userService.completeUserPasswordReset(userResetPasswordDto));
|
||||||
return new Response<>(userService.completeUserPasswordReset(key, userResetPasswordDto));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@PostMapping("/changePassword")
|
@PostMapping("/changePassword")
|
||||||
|
@ -14,6 +14,10 @@ public class UserResetPasswordDto {
|
|||||||
@Size(min = Constants.MIN_PASSWORD_LENGTH, max = 50)
|
@Size(min = Constants.MIN_PASSWORD_LENGTH, max = 50)
|
||||||
private String passwordConfirm;
|
private String passwordConfirm;
|
||||||
|
|
||||||
|
@NotEmpty
|
||||||
|
@Size(min = Constants.RESET_KEY_LENGTH)
|
||||||
|
private String resetKey;
|
||||||
|
|
||||||
public String getPassword() {
|
public String getPassword() {
|
||||||
return password;
|
return password;
|
||||||
}
|
}
|
||||||
@ -25,4 +29,8 @@ public class UserResetPasswordDto {
|
|||||||
public boolean isPasswordsValid() {
|
public boolean isPasswordsValid() {
|
||||||
return Objects.equals(password, passwordConfirm);
|
return Objects.equals(password, passwordConfirm);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String getResetKey() {
|
||||||
|
return resetKey;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -106,8 +106,7 @@ public class MailService {
|
|||||||
sendEmailFromTemplate(user, "activationEmail", Constants.MAIL_ACTIVATE);
|
sendEmailFromTemplate(user, "activationEmail", Constants.MAIL_ACTIVATE);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Async
|
public void sendPasswordResetMail(User user) throws MessagingException, MailException {
|
||||||
public void sendPasswordResetMail(User user) {
|
|
||||||
sendEmailFromTemplate(user, "passwordResetEmail", Constants.MAIL_RESET);
|
sendEmailFromTemplate(user, "passwordResetEmail", Constants.MAIL_RESET);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -118,6 +117,5 @@ public class MailService {
|
|||||||
@Async
|
@Async
|
||||||
public void sendChangePasswordMail(User user) {
|
public void sendChangePasswordMail(User user) {
|
||||||
sendEmailFromTemplate(user, "passwordChangeEmail", Constants.MAIL_CHANGE_PASSWORD);
|
sendEmailFromTemplate(user, "passwordChangeEmail", Constants.MAIL_CHANGE_PASSWORD);
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -236,7 +236,7 @@ public class UserService implements UserDetailsService {
|
|||||||
mailService.sendChangePasswordMail(user);
|
mailService.sendChangePasswordMail(user);
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean requestUserPasswordReset(String email) {
|
public boolean requestUserPasswordReset(String email) {
|
||||||
User user = userRepository.findOneByEmailIgnoreCase(email);
|
User user = userRepository.findOneByEmailIgnoreCase(email);
|
||||||
if (user == null) {
|
if (user == null) {
|
||||||
throw new UserNotFoundException(email);
|
throw new UserNotFoundException(email);
|
||||||
@ -247,23 +247,30 @@ public class UserService implements UserDetailsService {
|
|||||||
user.setResetKey(UserUtils.generateResetKey());
|
user.setResetKey(UserUtils.generateResetKey());
|
||||||
user.setResetDate(new Date());
|
user.setResetDate(new Date());
|
||||||
user = userRepository.save(user);
|
user = userRepository.save(user);
|
||||||
mailService.sendPasswordResetMail(user);
|
try {
|
||||||
|
mailService.sendPasswordResetMail(user);
|
||||||
|
} catch (MessagingException | MailException e) {
|
||||||
|
throw new UserSendingMailException(email);
|
||||||
|
}
|
||||||
log.debug("Created Reset Password Request for User: {}", user.getLogin());
|
log.debug("Created Reset Password Request for User: {}", user.getLogin());
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean completeUserPasswordReset(String key, UserResetPasswordDto userResetPasswordDto) {
|
public boolean completeUserPasswordReset(UserResetPasswordDto userResetPasswordDto) {
|
||||||
if (!userResetPasswordDto.isPasswordsValid()) {
|
if (!userResetPasswordDto.isPasswordsValid()) {
|
||||||
throw new UserPasswordsNotValidOrNotMatchException("");
|
throw new UserPasswordsNotValidOrNotMatchException("Пароли не совпадают");
|
||||||
}
|
}
|
||||||
User user = userRepository.findOneByResetKey(key);
|
User user = userRepository.findOneByResetKey(userResetPasswordDto.getResetKey());
|
||||||
if (user == null) {
|
if (user == null) {
|
||||||
throw new UserResetKeyError(key);
|
throw new UserResetKeyError(userResetPasswordDto.getResetKey());
|
||||||
}
|
}
|
||||||
user.setPassword(passwordEncoder.encode(userResetPasswordDto.getPassword()));
|
user.setPassword(passwordEncoder.encode(userResetPasswordDto.getPassword()));
|
||||||
user.setResetKey(null);
|
user.setResetKey(null);
|
||||||
user.setResetDate(null);
|
user.setResetDate(null);
|
||||||
user = userRepository.save(user);
|
user = userRepository.save(user);
|
||||||
|
|
||||||
|
mailService.sendChangePasswordMail(user);
|
||||||
|
|
||||||
log.debug("Reset Password for User: {}", user.getLogin());
|
log.debug("Reset Password for User: {}", user.getLogin());
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -1,23 +1,19 @@
|
|||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html xmlns:th="http://www.thymeleaf.org">
|
<html xmlns:th="http://www.thymeleaf.org">
|
||||||
<head>
|
<head>
|
||||||
<title>Password reset</title>
|
<title>Восстановление пароля</title>
|
||||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
|
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
|
||||||
<link rel="shortcut icon" th:href="@{|${baseUrl}/favicon.ico|}"/>
|
<link rel="shortcut icon" th:href="@{|${baseUrl}/favicon.ico|}"/>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<p>
|
<p>
|
||||||
Dear <span th:text="${user.firstName + ' ' + user.lastName}">Ivan Ivanov</span>
|
Дорогой <span th:text="${user.firstName + ' ' + user.lastName}">Ivan Ivanov</span>
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
For your account a password reset was requested, please click on the URL below to
|
Ваш ключ для восстановления пароля <span th:text="${user.resetKey}"></span>
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
<a th:href="@{|${baseUrl}/reset?key=${user.resetKey}|}"
|
С уважением,
|
||||||
th:text="@{|${baseUrl}/reset?key=${user.resetKey}|}">Reset Link</a>
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
Regards,
|
|
||||||
<br/>
|
<br/>
|
||||||
<em>Balance Team.</em>
|
<em>Balance Team.</em>
|
||||||
</p>
|
</p>
|
||||||
|
3
src/main/resources/public/css/base.css
Normal file
3
src/main/resources/public/css/base.css
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
.loader {
|
||||||
|
padding-left:50%
|
||||||
|
}
|
BIN
src/main/resources/public/img/main/ajax-loader.gif
Normal file
BIN
src/main/resources/public/img/main/ajax-loader.gif
Normal file
Binary file not shown.
After Width: | Height: | Size: 673 B |
@ -35,10 +35,7 @@ function changePassword() {
|
|||||||
|
|
||||||
function inviteUser() {
|
function inviteUser() {
|
||||||
email = document.getElementById("email").value;
|
email = document.getElementById("email").value;
|
||||||
re = /\S+@\S+\.\S+/;
|
if (!isEmailValid(email)) {
|
||||||
|
|
||||||
|
|
||||||
if (!re.test(email)) {
|
|
||||||
showFeedbackMessage("Некорректный почтовый ящик", MessageTypesEnum.WARNING);
|
showFeedbackMessage("Некорректный почтовый ящик", MessageTypesEnum.WARNING);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -55,4 +52,77 @@ function inviteUser() {
|
|||||||
showFeedbackMessage(errorData.responseJSON.error.message, MessageTypesEnum.WARNING)
|
showFeedbackMessage(errorData.responseJSON.error.message, MessageTypesEnum.WARNING)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function requestResetPassword() {
|
||||||
|
email = document.getElementById("emailReset").value
|
||||||
|
|
||||||
|
if (!isEmailValid(email)) {
|
||||||
|
showFeedbackMessage("Некорректный почтовый ящик", MessageTypesEnum.WARNING);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
document.getElementById("dvloader").hidden = false;
|
||||||
|
|
||||||
|
$.ajax({
|
||||||
|
url:"/api/1.0/users/password-reset-request?email=" + email,
|
||||||
|
contentType: "application/json; charset=utf-8",
|
||||||
|
method: "POST",
|
||||||
|
success: function() {
|
||||||
|
showFeedbackMessage("Проверочный код был отправлен на указанный почтовый ящик", MessageTypesEnum.SUCCESS)
|
||||||
|
document.getElementById("passwordNew").hidden = false
|
||||||
|
document.getElementById("passwordConfirm").hidden = false
|
||||||
|
document.getElementById("btnReset").hidden = false
|
||||||
|
document.getElementById("resetKey").hidden = false
|
||||||
|
document.getElementById("emailReset").hidden = true
|
||||||
|
document.getElementById("btnSend").hidden = true
|
||||||
|
document.getElementById("dvloader").hidden = true;
|
||||||
|
|
||||||
|
},
|
||||||
|
error: function(errorData) {
|
||||||
|
showFeedbackMessage(errorData.responseJSON.error.message, MessageTypesEnum.WARNING)
|
||||||
|
document.getElementById("dvloader").hidden = true;
|
||||||
|
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
function resetPassword() {
|
||||||
|
passwordNew = document.getElementById("passwordNew").value;
|
||||||
|
passwordConfirm = document.getElementById("passwordConfirm").value;
|
||||||
|
resetKey = document.getElementById("resetKey").value;
|
||||||
|
|
||||||
|
if ([passwordNew, passwordConfirm, resetKey].includes("")) {
|
||||||
|
showFeedbackMessage("Заполните все поля", MessageTypesEnum.WARNING);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (passwordNew != passwordConfirm) {
|
||||||
|
showFeedbackMessage("Пароли не совпадают", MessageTypesEnum.WARNING);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$.ajax({
|
||||||
|
url:"/api/1.0/users/password-reset",
|
||||||
|
contentType: "application/json; charset=utf-8",
|
||||||
|
method: "POST",
|
||||||
|
data: JSON.stringify({
|
||||||
|
"password": passwordNew,
|
||||||
|
"passwordConfirm": passwordConfirm,
|
||||||
|
"resetKey": resetKey,
|
||||||
|
}),
|
||||||
|
success: function() {
|
||||||
|
showFeedbackMessage("Пользователь был успешно приглашен", MessageTypesEnum.SUCCESS)
|
||||||
|
window.location.href = "/login"
|
||||||
|
},
|
||||||
|
error: function(errorData) {
|
||||||
|
showFeedbackMessage(errorData.responseJSON.error.message, MessageTypesEnum.WARNING)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function isEmailValid(email) {
|
||||||
|
re = /\S+@\S+\.\S+/;
|
||||||
|
return re.test(email)
|
||||||
}
|
}
|
@ -1,9 +1,10 @@
|
|||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html lang="en"
|
<html lang="en"
|
||||||
xmlns:th="http://www.thymeleaf.org"
|
|
||||||
xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
|
xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
|
||||||
layout:decorator="default">
|
layout:decorator="default" xmlns:th="http://www.w3.org/1999/xhtml">
|
||||||
<head>
|
<head>
|
||||||
|
<script src="/js/users.js"></script>
|
||||||
|
<link rel="stylesheet" href="../css/base.css"/>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<nav layout:fragment="navbar">
|
<nav layout:fragment="navbar">
|
||||||
@ -12,46 +13,47 @@
|
|||||||
class="fa fa-plane fa-4" aria-hidden="true"></i> Balance</span></a>
|
class="fa fa-plane fa-4" aria-hidden="true"></i> Balance</span></a>
|
||||||
</div>
|
</div>
|
||||||
</nav>
|
</nav>
|
||||||
<div class="container" layout:fragment="content">
|
<div layout:fragment="content">
|
||||||
<form id="reset-form" method="post" class="margined-top-10">
|
<section class="bg-light" id="portfolio">
|
||||||
<fieldset>
|
|
||||||
<div class="form-group">
|
|
||||||
<input type="email" name="email" id="email" class="form-control"
|
|
||||||
placeholder="E-Mail" required="true" autofocus="autofocus"/>
|
|
||||||
</div>
|
|
||||||
<button type="submit" class="btn btn-success btn-block">Сбросить пароль</button>
|
|
||||||
<div class="form-group">
|
|
||||||
<small class="form-text text-muted">
|
|
||||||
<a href="/login">Вернуться к странице входа</a>
|
|
||||||
</small>
|
|
||||||
</div>
|
|
||||||
</fieldset>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
<th:block layout:fragment="data-scripts">
|
|
||||||
<script type="text/javascript">
|
|
||||||
/*<![CDATA[*/
|
|
||||||
$(document).ready(function () {
|
|
||||||
$("#reset-form").submit(function () {
|
|
||||||
var email = $("#email").val();
|
|
||||||
if (isEmpty(email)) {
|
|
||||||
showFeedbackMessage("Адрес электронной почты не задан", MessageTypesEnum.DANGER);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
postToRest(urlUsersPasswordResetRequest + "?email=" + email, null,
|
|
||||||
function () {
|
|
||||||
showFeedbackMessage("Запрос на смену пароля отправлен");
|
|
||||||
},
|
|
||||||
function () {
|
|
||||||
$("#email").val("");
|
|
||||||
}
|
|
||||||
);
|
|
||||||
return false;
|
|
||||||
});
|
|
||||||
});
|
|
||||||
/*]]>*/
|
|
||||||
|
|
||||||
</script>
|
<div class="container">
|
||||||
</th:block>
|
<div class="row justify-content-md-center">
|
||||||
|
<div class="col-lg-6">
|
||||||
|
<div class="form-group">
|
||||||
|
<input type="text" name="email" id="emailReset" class="form-control"
|
||||||
|
placeholder="E-Mail"/>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<input type="password" name="email" id="passwordNew" class="form-control"
|
||||||
|
placeholder="Новый пароль" hidden="true"/>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<input type="password" name="email" id="passwordConfirm" class="form-control"
|
||||||
|
placeholder="Подтвердите пароль" hidden="true"/>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<input type="text" name="email" id="resetKey" class="form-control"
|
||||||
|
placeholder="Код подтверждения" hidden="true"/>
|
||||||
|
</div>
|
||||||
|
<div id="dvloader" class="loader" hidden="true"><img src="../img/main/ajax-loader.gif" /></div>
|
||||||
|
<button id="btnSend" type="button" onclick="requestResetPassword()"
|
||||||
|
class="btn btn-success btn-block">
|
||||||
|
Отправить код подтверждения
|
||||||
|
</button>
|
||||||
|
<button id="btnReset" hidden="true" type="button" onclick="resetPassword()"
|
||||||
|
class="btn btn-success btn-block">
|
||||||
|
Сбросить
|
||||||
|
пароль
|
||||||
|
</button>
|
||||||
|
<div class="form-group">
|
||||||
|
<small class="form-text text-muted">
|
||||||
|
<a href="/login">Вернуться к странице входа</a>
|
||||||
|
</small>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
</div>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
Loading…
Reference in New Issue
Block a user