organize/프로젝트

1차 프로젝트 추가 - 암호화2

001cloudid 2024. 12. 23. 17:27
728x90
구현하려고 했던 것

회원 가입 시 사용자가 입력한 평문 비밀번호를 암호화하여 DBMS Member 테이블의 pw 컬럼에 저장하고, 로그인 시 회원가입 시 입력한 비밀번호로 로그인 성공

 

발생한 문제

DBMS Member 테이블에 pw컬럼에 암호화된 비밀번호가 입력, 하지만 로그인 시 회원가입 시 입력한 비밀번호로 로그인 실패, 암호화 된 비밀번호를 입력해야 로그인 성공

 

문제 해결을 위한 접근

로그인 할 때 사용자가 입력한 평문 비밀번호와 DB에 저장된 암호화된 비밀번호를 직접 비교.

Spring Security의 PasswordEncoder 인터페이스에서 제공하는 matches() 메소드를 사용

 

boolean matches(CharSequence rawPassword, String encodedPassword);

matches() : 입력받은 평문 비밀번호와 DB에 저장된 암호화된 비밀번호를 비교하여 두 값이 일치하는지 확인

rawPassword : 사용자가 입력한 평문 비밀번호

encodedPassword : DB에 저장된 암호화 비밀번호. 보통 encode() 메소드를 통해 암호화된 후 저장


MemberService.java

기존 로그인 시 userCheck() 메소드

	public MemberDTO userCheck(MemberDTO memberDTO) {
		System.out.println("MemberService userCheck()");
		return memberDAO.userCheck(memberDTO);
	}

 

MemberService 에서 로그인 처리 과정 로직을 수정

	public MemberDTO userCheck(MemberDTO memberDTO) {
		System.out.println("MemberService userCheck()");
		
		 // DB에서 회원 정보를 가져옴
        MemberDTO encodedPassword = memberDAO.userCheck(memberDTO);

        // DB에서 가져온 암호화된 비밀번호와 비교
        if (encodedPassword != null && passwordEncoder.matches(memberDTO.getPw(), encodedPassword.getPw())) {
            return encodedPassword;
        }
        return null;
	}

 

 

MemberMapper.xml

기존 쿼리

	<select id="userCheck" resultType="com.itwillbs.domain.MemberDTO">
		select * 
		from member
		where id = #{id} and pw = #{pw} 
	</select>

 

수정된 쿼리

	<select id="userCheck" resultType="com.itwillbs.domain.MemberDTO">
		select * 
		from member
		where id = #{id}
	</select>

 

쿼리에서 pw 부분이 없어져야 함. 비밀번호를 직접 비교하는 방식이 아닌 Spring Security의 PasswordEncoder를 사용하여 암호화된 비밀번호를 비교하기 때문임. 평문 비밀번호를 DB 쿼리에서 바로 비교하지 않고, 암호화된 값과 비교하는 방식으로 동작함.

 

암호화된 비밀번호 입력

 

평문 암호입력(1234)

회원가입 시 비밀번호 암호화 완료


+

회원 수정 → 비밀 번호 변경 시 마찬가지로 암호화를 해줘야 함

 

MemberService.java

기존 회원 정보 메소드

	// 회원 정보 수정
	public void updateMember(MemberDTO memberDTO) {
		System.out.println("MemberService updateMember()");
		memberDAO.updateMember(memberDTO);
	}

 

수정된 회원 정보 메소드

	// 회원 정보 수정
	public void updateMember(MemberDTO memberDTO) {
		System.out.println("MemberService updateMember()");
		
		// 비밀번호가 변경되었으면 암호화
		if(memberDTO.getPw() != null && !memberDTO.getPw().isEmpty()) {
			String encodedPassword = passwordEncoder.encode(memberDTO.getPw());
			memberDTO.setPw(encodedPassword);
		}

		memberDAO.updateMember(memberDTO);
	}

또 다른 문제 사항

1. 기존에 회원 가입되어 있던(암호화를 하기 전) 회원들의 로그인이 되지 않음

→ 해결 방법

MemberService의 userCheck() 메소드에 조건문을 주어 기존 사용자들도 로그인이 가능하게 구현

 

MemberService.java
	public MemberDTO userCheck(MemberDTO memberDTO) {
		System.out.println("MemberService userCheck()");
		
		 // DB에서 회원 정보를 가져옴
        MemberDTO encodedPassword = memberDAO.userCheck(memberDTO);

        // DB에서 가져온 암호화된 비밀번호와 비교
        if (encodedPassword != null && passwordEncoder.matches(memberDTO.getPw(), encodedPassword.getPw())) {
            return encodedPassword;
        }
        return null;
	}

위 코드를 아래 코드로 수정

	public MemberDTO userCheck(MemberDTO memberDTO) {
		System.out.println("MemberService userCheck()");
		// DB에서 회원 정보를 가져옴
		MemberDTO encodedPassword = memberDAO.userCheck(memberDTO);
		// DB에서 가져온 비밀번호가 평문인 경우
		if (encodedPassword != null && !encodedPassword.getPw().startsWith("{bcrypt}")) { // 기존 비밀번호가 평문인 경우
			// 기존 비밀번호를 암호화하여 업데이트
			String encodedPw = passwordEncoder.encode(encodedPassword.getPw());
			encodedPassword.setPw(encodedPw);
			// 암호화된 비밀번호로 업데이트 (DB에 반영)
			memberDAO.updateMemberPassword(encodedPassword);
		}
		// DB에서 가져온 암호화된 비밀번호와 입력된 비밀번호를 비교
		if (encodedPassword != null && passwordEncoder.matches(memberDTO.getPw(), encodedPassword.getPw())) {
			return encodedPassword;
		}
		return null; // 로그인 실패
	}

 

 

MemberDAO.java
	public void updateMemberPassword(MemberDTO memberDTO) {
	    System.out.println("MemberDAO updateMemberPassword()");
	    // 비밀번호를 암호화된 형태로 DB에 업데이트
	    sqlSession.update(namespace + ".updateMemberPassword", memberDTO);
	}

 

 

MemberMapper.xml
	<update id="updateMemberPassword" parameterType="com.itwillbs.domain.MemberDTO">
		update member
		set pw = #{pw}
		where id = #{id}
	</update>

 

코드를 수정하면

기존의 평문 비밀번호로 로그인을 하면 자동으로 암호화된 암호로 변경됨 

 

1-1 다음 문제 사항

비밀번호가 암호화가 되지 않은 회원이 로그인 했을 때 평문 비밀번호가 암호화 비밀번호로 변경이 됨

다만, 비밀번호가 변경 된 후 로그아웃 후 로그인 할 때 평문 비밀번호, 암호화된 비밀번호로 로그인이 안됨


또 다른 문제 사항 2

비밀번호 찾기에서 암호화된 비밀번호는 평문으로 된 비밀번호로 복호화가 불가능하므로 비밀번호 찾기를 시도하고 이후 비밀번호 바꿀 수 있게 함

 

findPW.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
	pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>공티 새 비밀번호 설정</title>
<style type="text/css">
#idForm {
	max-width: 400px;
	margin: auto;
	background-color: #fff;
	padding: 20px;
	border-radius: 8px;
	box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
}

button {
	background-color: blue;
	color: white;
	padding: 10px;
	border: none;
	border-radius: 4px;
	cursor: pointer;
	width: 100px;
}
</style>
</head>
<jsp:include page="../inc/top.jsp" />
<body>
	<br>
	<br>
	<br>
	<br>
	<br>
	<br>
	<br>
	<div id="idForm">
		<form action="${pageContext.request.contextPath}/main/findPwPro" method="post">
			<h2>새로운 비밀번호</h2>
			<input type="hidden" name="id" value="${memberDTO2.id}">
			새로운 비밀번호 <input type="password" name="pw" id="pw"><br>
			비밀번호 확인 <input type="password" id="pw2"><br>
			<center>
				<button type="submit" onclick="return validatePw()">확인</button>
				<button type="reset" value="취소" class="cancel"
					onclick="history.go(-2)">돌아가기</button>
			</center>
		</form>
	</div>
	<br>
	<br>
	<br>
	<br>
	<br>
	<br>
	<br>
	<jsp:include page="../inc/bottom.jsp" />
	
	<script type="text/javascript">
		// 비밀번호 확인 함수
		function validatePw(){
			var pw = document.getElementById("pw").value;
			var pw2 = document.getElementById("pw2").value;
			
			if(pw !== pw2){
				alert("입력한 비밀번호가 다릅니다.");
				document.getElementById("pw").focus();
				return false;
			}
			return true;
		}
	
	</script>
</body>
</html>
pwChange.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>공티 비밀번호 변경 완료</title>
<style type="text/css">
#idForm {
	max-width: 400px;
	margin: auto;
	background-color: #fff;
	padding: 20px;
	border-radius: 8px;
	box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
}

button {
	background-color: blue;
	color: white;
	padding: 10px;
	border: none;
	border-radius: 4px;
	cursor: pointer;
	width: 100px;
}
</style>
</head>
<jsp:include page="../inc/top.jsp" />
<body>
	<br>
	<br>
	<br>
	<br>
	<br>
	<br>
	<br>
	<div id="idForm">
		<form action="${pageContext.request.contextPath}/main/findPwPro" method="post">
			<h2>비밀번호가 성공적으로 변경되었습니다.</h2>
			<center>
				<a href="${pageContext.request.contextPath}/main/login">로그인 페이지로 가기</a>
			</center>
		</form>
	</div>
	<br>
	<br>
	<br>
	<br>
	<br>
	<br>
	<br>
	<jsp:include page="../inc/bottom.jsp" />

</body>
</html>

 

MemberController.java
@PostMapping("/main/findPW")
public String searchPWPro(MemberDTO memberDTO, Model model) {
System.out.println("MemberController searchPWPro()");
MemberDTO memberDTO2 = new MemberDTO();
memberDTO2 = memberService.userCheckPW(memberDTO);
if (memberDTO2 != null) {
model.addAttribute("memberDTO2", memberDTO2);
return "/main/findPW";
} else {
return "/main/msg_search";
}
}

@PostMapping("/main/findPwPro")
public String resetPwPro(MemberDTO memberDTO) {
System.out.println("MemberController resetPwPro()");
memberService.updateMemberPassword(memberDTO);
return "/main/pwChange";
}
MemberService.java
	public void updateMemberPassword(MemberDTO memberDTO) {
		System.out.println("MemberService updateMemberPassword()");
		memberDAO.updateMemberPassword(memberDTO);
	}

 

 

비밀번호가 암호화가 되지 않은 회원 비밀번호 찾기

qwer로 변경

 

변경 후 DB확인

비밀번호가 변경됨. 하지만 새로운 비밀번호 qwer로 로그인을 시도했을 때 로그인이 안됨. 암호화된 비밀번호로 로그인이 되는 문제

MemberService.java
	public MemberDTO userCheck(MemberDTO memberDTO) {
		System.out.println("MemberService userCheck()");
		
		System.out.println("평문으로 입력한 비밀번호 : " + memberDTO.getPw());
	
		// DB에서 회원 정보를 가져옴
		MemberDTO encodedPassword = memberDAO.userCheck(memberDTO);
		// DB에서 가져온 비밀번호가 평문인 경우
		if (encodedPassword != null && !encodedPassword.getPw().startsWith("{bcrypt}")) { // 기존 비밀번호가 평문인 경우
			return encodedPassword;
		}
		// DB에서 가져온 암호화된 비밀번호와 입력된 비밀번호를 비교
		if (encodedPassword != null && passwordEncoder.matches(memberDTO.getPw(), encodedPassword.getPw())) {
			return encodedPassword;
		}
		return null; // 로그인 실패
	}

 

728x90