원래는 아이디, 닉네임, 비밀번호 각각 수정하려고 했지만 어째선지 별 노력을 해도 각각 수정했을 때 DB에는 회원 정보가 변경됐지만 변경된 세션은 등록되지 않았다 ^-^.. 자꾸 로그아웃이 됐다....하.......
그래서 일단 닉네임과 비밀번호를 한꺼번에 수정하도록 구현했다.
회원 수정
을 클릭한다.비밀번호를 입력
하여 일치해야만 회원 수정 페이지로 진입한다.중복 검사
는 서버를 통해 검사한다.내 정보
페이지로 반환된다. /** 회원 수정하기 전 비밀번호 확인 **/
@GetMapping("/checkPwd")
public String checkPwdView(){
return "member/check-pwd";
}
...
<div class="card-body">
<div class="text-start">
<input type="hidden" th:name="_csrf" th:value="${_csrf.token}"/>
<div class="input-group input-group-outline my-3">
<label class="form-label">비밀번호 확인</label>
<input type="password" id="password" name="password" class="form-control">
</div>
</div>
<div class="text-center">
<button class="btn bg-gradient-primary w-100 my-4 mb-2" id="checkPwd"> 비밀번호 확인</button>
...
<script>
$('#checkPwd').click(function() {
const checkPassword = $('#password').val();
if(!checkPassword || checkPassword.trim() === ""){
alert("비밀번호를 입력하세요.");
} else{
$.ajax({
type: 'GET',
url: '/rest/checkPwd',
data: {'checkPassword': checkPassword},
datatype: "text"
}).done(function(result){
console.log(result);
if(result){
console.log("비밀번호 일치");
window.location.href="/settings/update";
} else if(!result){
console.log("비밀번호 틀림");
// 비밀번호가 일치하지 않으면
alert("비밀번호가 맞지 않습니다.");
window.location.reload();
}
}).fail(function(error){
alert(JSON.stringify(error));
})
}
});
</script>
rest/checkPwd
컨트롤러에 GET
방식으로 전송하여 결괏값을 반환받는다. /** 회원 수정 전 비밀번호 확인 **/
@GetMapping("/checkPwd")
public boolean checkPassword(@AuthenticationPrincipal UserAdapter user,
@RequestParam String checkPassword,
Model model){
log.info("checkPwd 진입");
Long member_id = user.getMemberDto().getId();
return memberService.checkPassword(member_id, checkPassword);
}
memberService
에 파라미터로 넘긴다. /** 비밀번호 일치 확인 **/
@Override
public boolean checkPassword(Long member_id, String checkPassword) {
Member member = memberRepository.findById(member_id).orElseThrow(() ->
new IllegalArgumentException("해당 회원이 존재하지 않습니다."));
String realPassword = member.getPassword();
boolean matches = encoder.matches(checkPassword, realPassword);
return matches;
}
matches
를 이용하여 비밀번호가 일치하는지 확인한다.<div class="col-lg-6">
<div class="row justify-content-start">
<form style="margin: auto" th:object="${member}">
<input type="hidden" th:name="_csrf" th:value="${_csrf.token}"/>
<input type="hidden" th:id="userId" th:value="*{id}">
<h3>아이디</h3>
<div class="input-group input-group-static mb-4">
<label style="font-weight: bold" th:for="username">아이디</label>
<input type="text" th:field="*{username}" th:value="*{username}" class="form-control"
th:id="username" readonly>
</div>
<h3>이메일</h3>
<div class="input-group input-group-static mb-4">
<label style="font-weight: bold" th:for="email">아이디</label>
<input type="text" th:field="*{email}" th:value="*{email}" class="form-control"
th:id="email" readonly>
</div>
<h3>닉네임 변경</h3>
<div class="input-group input-group-static mb-4">
<label style="font-weight: bold" th:for="username">기존 닉네임</label>
<input type="text" th:field="*{nickname}" th:value="*{nickname}" class="form-control"
th:id="nickname" readonly>
</div>
<div class="input-group input-group-static mb-4">
<label style="font-weight: bold" th:for="nickname">변경할 닉네임 입력</label>
<input type="text" th:id="newNickname" th:name="newNickname"
placeholder="변경할 닉네임을 입력하세요." class="form-control" >
</div>
<h3>비밀번호 변경</h3>
<div class="input-group input-group-static mb-4">
<label style="font-weight: bold" th:for="password">변경할 비밀번호 입력</label>
<input type="password" th:id="password" th:name="password"
placeholder="변경할 비밀번호를 입력하세요." class="form-control" >
</div>
</form>
<button id="memberUpdate" class="btn bg-gradient-primary" value="회원 정보 수정"
th:onclick="memberUpdate()">회원 정보 수정</button>
</div>
</div>
<script>
function memberUpdate(){
const data = {
id: $('#userId').val(),
username: $('#username').val(),
nickname: $('#newNickname').val(),
password: $('#password').val()
};
// 공백 및 빈 문자열 체크
if(!data.nickname || data.nickname.trim() === "" || !data.password || data.password.trim() === ""){
alert("공백 또는 입력하지 않은 부분이 있습니다.");
return false;
}
// 유효성 검사
else if(!/^[가-힣a-zA-Z0-9]{2,10}$/.test(data.nickname)){
alert("닉네임은 특수문자를 포함하지 않은 2~10자리여야 합니다.");
$('#newNickname').focus();
return false;
}
else if(!/(?=.*[0-9])(?=.*[a-zA-Z])(?=.*\W)(?=\S+$).{8,16}/.test(data.password)){
alert("비밀번호는 8~16자리수여야 합니다. 영문 대소문자, 숫자, 특수문자를 1개 이상 포함해야 합니다.");
$('#password').focus();
return false;
}
const confirmCheck = confirm("수정하시겠습니까?");
if(confirmCheck == true){
$.ajax({
type: 'PUT',
url: '/rest/member',
contentType: 'application/json; charset=utf-8',
data: JSON.stringify(data)
}).done(function(result){
if(result){
alert("회원 수정이 완료되었습니다.");
window.location.href="/mypage";
} else{
alert("이미 사용 중인 닉네임입니다. 다시 입력해주세요.");
$('#newNickname').focus();
}
}).fail(function(error){
alert(JSON.stringify(error));
});
}
}
</script>
rest/member
컨트롤러에 put
메소드로 ajax 통신을 진행한다. /** 회원 정보 수정 **/
@PutMapping("/member")
public boolean update(@RequestBody MemberDto.RequestDto dto) {
log.info("MemberRestController 진입");
if(memberService.checkNickname(dto.getId(), dto.getNickname())){
log.info("중복 닉네임");
return false;
} else{
log.info("사용 가능한 닉네임");
// 회원 정보 수정
memberService.userInfoUpdate(dto);
/** ========== 변경된 세션 등록 ========== **/
/* 1. 새로운 UsernamePasswordAuthenticationToken 생성하여 AuthenticationManager 을 이용해 등록 */
Authentication authentication = authenticationManager.authenticate(
new UsernamePasswordAuthenticationToken(dto.getUsername(), dto.getPassword())
);
/* 2. SecurityContextHolder 안에 있는 Context를 호출해 변경된 Authentication으로 설정 */
SecurityContextHolder.getContext().setAuthentication(authentication);
return true;
}
}
/** 닉네임 중복 체크 **/
@Override
public boolean checkNickname(Long member_id, String nickname) {
if(memberRepository.existsByNickname(nickname)){
if(memberRepository.findByNickname(nickname).getId() == member_id){
// 입력 받은 닉네임의 회원 id와 일치한다면 즉, 현재 닉네임을 그대로 입력한 경우
return false;
} else{
// 다른 사람이 사용하고 있는 닉네임이라면
return true;
}
} else{
// 중복된 닉네임이 아니라면
return false;
}
}
/** 회원 수정 **/
@Override
public void userInfoUpdate(MemberDto.RequestDto memberDto) {
/* 회원 찾기 */
Member member = memberRepository.findById(memberDto.toEntity().getId()).orElseThrow(() ->
new IllegalArgumentException("해당 회원이 존재하지 않습니다."));
/* 수정한 비밀번호 암호화 */
String encryptPassword = encoder.encode(memberDto.getPassword());
member.update(memberDto.getNickname(), encryptPassword); // 회원 수정
log.info("회원 수정 성공");
}
public void update(String nickname, String password){
this.nickname = nickname;
this.password = password;
}
/** ========== 변경된 세션 등록 ========== **/
/* 1. 새로운 UsernamePasswordAuthenticationToken 생성하여 AuthenticationManager 을 이용해 등록 */
Authentication authentication = authenticationManager.authenticate(
new UsernamePasswordAuthenticationToken(dto.getUsername(), dto.getPassword())
);
/* 2. SecurityContextHolder 안에 있는 Context를 호출해 변경된 Authentication으로 설정 */
SecurityContextHolder.getContext().setAuthentication(authentication);
UsernamePasswordAuthenticationToken
을 만들고 AuthenticationManager
를 사용해 등록해준다.SecurityContextHolder
안에 있는 Context를 호출해 변경된 Authentication
을 설정해준다.이 때, AuthenticationManager
을 사용하기 위해 SecurityConfig에서 bean으로 등록해준다.
@Configuration
@EnableWebSecurity
@RequiredArgsConstructor
public class SecurityConfig extends WebSecurityConfigurerAdapter {
...
/** AuthenticationManager 빈 등록 **/
@Bean
@Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
}