스프링과 JPA 기반 웹 애플리케이션 개발 #32 패스워드 수정
스프링과 JPA 기반 웹 애플리케이션 개발 #32 패스워드 수정
해당 내용은 인프런, 스프링과 JPA 기반 웹 애플리케이션 개발의 강의 내용을 바탕으로 작성된 내용입니다.
강의를 학습하며 요약한 내용을 출처를 표기하고 블로깅 또는 문서로 공개하는 것을 허용합니다 라는 원칙 하에 요약 내용을 공개합니다. 출처는 위에 언급되어있듯, 인프런, 스프링과 JPA 기반 웹 애플리케이션 개발입니다.
제가 학습한 소스코드는 https://github.com/n00nietzsche/jakestudy_webapp 에 지속적으로 업로드 됩니다. 매 커밋 메세지에 강의의 어디 부분까지 진행됐는지 기록해놓겠습니다.
패스워드 수정
- 패스워드 탭 활성화
- 새 패스워드와 새 패스워드 확인 값이 일치해야 한다.
- 패스워드 인코딩
- 둘 다 최소 8자에서 최대 50자 사이
- 사용자 정보를 변경하는 작업
- 서비스로 위임해서 트랜잭션 안에서 처리해야 한다.
- 또는
Detached
상태의 객체를 변경한 다음Repository
의.save()
를 호출해서 상태 변경 내용을 적용해도 된다.
PasswordForm 클래스 작성
@Data
public class PasswordForm {
@Length(min = 8, max = 50)
private String newPassword;
@Length(min = 8, max = 50)
private String newPasswordConfirm;
}
PasswordFormValidator 클래스 작성
@Component
public class PasswordFormValidator implements Validator {
@Override
public boolean supports(Class<?> aClass) {
// 매개변수로 해당 클래스/인터페이스를 상속/구현한 클래스가 들어온건지 확인
// Object 의 instanceOf 와 비슷하다.
return PasswordForm.class.isAssignableFrom(aClass);
}
@Override
public void validate(Object o, Errors errors) {
PasswordForm passwordForm = (PasswordForm) o;
if(!passwordForm.getNewPassword().equals(passwordForm.getNewPasswordConfirm())) {
errors.rejectValue("newPassword","wrong.value", "입력한 새 패스워드가 일치하지 않습니다.");
}
}
}
SettingsController에 PasswordFormValidator 적용
...
public class SettingsController {
private final AccountService accountService;
private final PasswordFormValidator passwordFormValidator;
...
@InitBinder("passwordForm")
// WebDataBinder 는 WebRequestParameters 를 받아서 Java Beans Object 형태로 변환해주는 역할을 한다.
// 여기에 .addValidator() 를 사용하면 특정 데이터가 들어왔을 때, 필터를 걸어 검증하는 역할이다.
// 특정 데이터를 판독하는 기준은 이전에 우리가 .supports() 메소드에 코딩했던 것과 같다.
// PasswordForm.class.isAssignableFrom(class) 로 해당 클래스/인터페이스를 구현/상속했는지 확인하고 검증에 들어간다.
public void initBinder(WebDataBinder webDataBinder) {
webDataBinder.addValidators(passwordFormValidator);
}
WebDataBinder
가 클라이언트로부터 WebRequestParameters
데이터를 받아서 자바 객체 (Java Beans Object
)로 매핑을 한 이후에 .addValidators()
에 등록된 Validator
로 검증을 한다.
이전에 PasswordFormValidator
에서 작성했던 .support()
는 WebDataBinder
에서 매핑한 클래스가 PasswordForm
에 할당 가능한 것(PasswordForm
클래스이거나, PasswordForm
클래스를 상속했거나)이면, 해당 검증을 지원한다고 한 것이다.
아래 .validate()
의 내용은 실제 검증 내용인데, 넘겨받은 객체를 PasswordForm
형태로 캐스팅한다. 넘겨받는 객체는 PasswordForm
타입으로 변환 가능할 수 밖에 없는데, 그 이유는 우리가 .support()
메소드에서 PasswordForm.class.isAssignableFrom()
으로 한번 검증을 했기 때문이다.
어찌됐든 PasswordForm
형태로 캐스팅하여, 해당 객체에 들어있는 두 패스워드가 일치하는지 검증한다.
기타 GET & POST 메소드들
@GetMapping(PASSWORD_MAPPING_PATH)
public String updatePasswordForm(@LoginAccount Account loginAccount,
Model model) {
model.addAttribute(loginAccount);
model.addAttribute(new PasswordForm());
return PASSWORD_MAPPING_PATH;
}
@PostMapping(PASSWORD_MAPPING_PATH)
public String updatePassword(
// Model 맨 뒤로 빼야한다.
// 왜냐하면 Attribute 들이 매핑돼서 결국 Model에 들어가야 하기 때문
@LoginAccount Account loginAccount,
@Valid @ModelAttribute PasswordForm passwordForm,
Errors errors,
RedirectAttributes redirectAttributes,
Model model
) {
if(errors.hasErrors()) {
model.addAttribute(loginAccount);
return PASSWORD_MAPPING_PATH;
}
accountService.updatePassword(loginAccount, passwordForm.getNewPassword());
redirectAttributes.addFlashAttribute("message", "패스워드를 변경했습니다.");
return "redirect:/" + PASSWORD_MAPPING_PATH;
위의 내용은 전에 배웠던 내용들과 같다. 단, 파라미터에서 Model
이 맨 뒤로 가는 이유는 항상 인지하자. ModelAttribute
등에서 적용된 내용을 Model
로 자동 매핑하여 옮겨줘야 하기 때문에, Model
이 맨 뒤로 가있지 않으면 400에러를 반환한다. 조심하자.
또한 패스워드 변경 부분은 데이터의 변화가 일어나는 부분이므로 accountService
에게 역할을 위임했다.
AccountService
updatePassword
public void updatePassword(Account account, String newPassword) {
account.setPassword(passwordEncoder.encode(newPassword));
}
패스워드를 넣을 때는 항상 인코딩 해서 넣는다.
password.html 작성
<!DOCTYPE html>
<html lang="en"
xmlns:th="http://www.thymeleaf.org"
xmlns:sec="http://www.thymeleaf.org/extras/spring-security">
<head>
<title>패스워드 수정</title>
<th:block th:replace="fragments :: headLibraryInjection"></th:block>
</head>
<body class="bg-light">
<th:block th:replace="fragments :: main-nav"></th:block>
<div class="alert alert-warning" role="alert" th:if="${account != null && !account?.emailVerified}">
스터디올래 가입을 완료하려면 <a href="#" th:href="@{/check-email}" class="alert-link">계정 인증 이메일을 확인</a>하세요
</div>
<div class="container">
<!-- row의 기본은 col-12만큼의 크기를 갖는다.-->
<div class="row mt-5 justify-content-center">
<div class="col-2">
<div th:replace="fragments :: settings-menu(currentMenu='profile')"></div>
</div>
<div class="col-8">
<div th:if="${message}" class="alert alert-info alert-dismissible fade show mt-3" role="alert">
<span th:text="${message}">메시지</span>
<button type="button" class="close" data-dismiss="alert" aria-label="Close">
<span aria-hidden="true">x</span>
</button>
</div>
<div class="row">
<h2 class="col-sm-12">패스워드 변경</h2>
</div>
<div class="row mt-3">
<form class="needs-validation col-12" action="#" th:action="@{/settings/password}"
th:object="${passwordForm}" method="post" novalidate>
<div class="form-group">
<label for="newPassword">새 패스워드</label>
<input id="newPassword" type="password" th:field="*{newPassword}"
class="form-control" aria-describedby="newPasswordHelp" required min="8" max="50">
<small id="newPasswordHelp" class="form-text text-muted">
새 패스워드를 입력하세요.
</small>
<small class="invalid-feedback">패스워드를 입력하세요.</small>
<small class="form-text text-danger" th:if="${#fields.hasErrors('newPassword')}"
th:errors="*{newPassword}">
패스워드 너무 길어요.
</small>
</div>
<div class="form-group">
<label for="newPasswordConfirm">새 패스워드</label>
<input id="newPasswordConfirm" type="password" th:field="*{newPasswordConfirm}"
class="form-control" aria-describedby="newPasswordConfirmHelp" required min="8" max="50">
<small id="newPasswordConfirmHelp" class="form-text text-muted">
새 패스워드를 입력하세요.
</small>
<small class="invalid-feedback">패스워드를 입력하세요.</small>
<small class="form-text text-danger" th:if="${#fields.hasErrors('newPasswordConfirm')}"
th:errors="*{newPasswordConfirm}">
패스워드 너무 길어요.
</small>
</div>
<div class="form-group">
<button class="btn btn-primary btn-block" type="submit"
aria-describedby="submitHelp">수정하기
</button>
</div>
</form>
</div>
</div>
</div>
<th:block th:replace="fragments :: footer"></th:block>
</div>
<script th:replace="fragments :: form-validation"></script>
</body>
</html>
위와 같이 모든 공간을 다 쓸때는 col-sm-12
와 같은 클래스명에서 sm
과 같이 해상도에 관련된 부분은 안넣어도 된다는 것을 인지하자.
Author And Source
이 문제에 관하여(스프링과 JPA 기반 웹 애플리케이션 개발 #32 패스워드 수정), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://velog.io/@jakeseo_me/스프링과-JPA-기반-웹-애플리케이션-개발-32-패스워드-수정저자 귀속: 원작자 정보가 원작자 URL에 포함되어 있으며 저작권은 원작자 소유입니다.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)