프로젝트

비밀번호암호화 , 비밀번호찾기(임시비밀번호발급 및 메일전송) , 회원가입->인증번호인증->완료 순으로 바꿈 , 회원가입 ,pw찾기 , 개인정보수정 시 유효성검사추가

동곤일상 2025. 5. 16. 17:33
반응형

1)회원가입

1-1)가입버튼을 누르면 넘어가는 폼 컨트롤러 (registerNumChk ) + Bcrypt(암호화)

1-2) EmailUtil.sendNum() 메서드

1-3) 인증번호를 입력해 검증하는곳 registerNumChk (성공시 회원가입이될거임)

1-4) 회원가입 성공(registerSuccess) 

 

2) pw찾기 (findPw)

2-1) findPwProcess , getTempPw(임시비번생성알고리즘)

 

 


1) 회원가입 -> 인증번호인증 -> 완료

<body>
	<div class="card">
		<h4 class="text-center mb-4">회원가입</h4>
		<form action="registerNumChk" name="f" method="post"
			onsubmit="return input_check(this)">
			<input type="hidden" name="picture" value="">
			<!-- 업로드된 이미지의 이름이 들어갈태그 -->
			<div class="mb-3">
				<img src="" width="100" height="120" id="pic"><br> <font
					size="1"><a href="javascript:win_upload()">사진등록</a></font>
			</div>
			<div class="mb-3">
				<label for="id" class="form-label">아이디</label> <input type="text"
					class="form-control" id="id" name="id" value="가입시 자동부여" readonly>
			</div>
			<div class="mb-3">
				<label for="name" class="form-label">이름</label> <input type="text"
					class="form-control" id="name" name="name" placeholder="이름 입력">
			</div>
			<div class="mb-3">
				<label for="birth" class="form-label">생년월일</label> <input
					type="date" class="form-control" id="birth" name="birth"
					placeholder="생년월일">
			</div>
			<div class="mb-3">
				<label class="form-label">직급</label>
				<div class="form-check">
					<input class="form-check-input" type="radio" name="position"
						id="pro" value="pro" checked> <label
						class="form-check-label" for="pro">교수</label>
				</div>
				<div class="form-check">
					<input class="form-check-input" type="radio" name="position"
						id="stu" value="stu"> <label class="form-check-label"
						for="stu">학생</label>
				</div>
			</div>
			<div class="mb-3">
				<label for="major" class="form-label">전공 선택</label> <select
					class="form-select" id="major" name="deptId">
					<option selected value="none">전공</option>
					<c:forEach items="${dept}" var="s">
						<option value="${s.deptId}">${s.deptName}</option>
					</c:forEach>
					<!-- <option value="Computer Science">컴퓨터공학과</option>
                <option value="Electrical Engineering">전자공학과</option>
                <option value="Mechanical Engineering">기계공학과</option>
                <option value="Business Administration">경영학과</option> -->

				</select>
			</div>
			<div class="mb-3">
				<label for="password" class="form-label">비밀번호</label> <input
					type="password" class="form-control" id="password" name="password"
					placeholder="비밀번호 입력" onkeyup="passwordChk(this)"> <font
					id="passValid"></font>

			</div>
			<div class="mb-3">
				<label for="confirmPassword" class="form-label">비밀번호 확인</label> <input
					type="password" class="form-control" id="confirmPassword"
					name="confirmPassword" placeholder="비밀번호 확인"
					onkeyup="re_passwordChk(this)"> <font id="pEqulasCp"></font>

			</div>
			<div class="mb-3">
				<label for="phone" class="form-label">전화번호</label> <input
					type="text" class="form-control" id="phone" name="phone"
					placeholder="전화번호 입력" onkeyup="phoneChk(this)"> <font
					id='phoneValid'></font>
			</div>
			<div class="mb-3">
				<label for="email" class="form-label">이메일</label> <input
					type="email" class="form-control" id="email" name="email"
					placeholder="이메일 입력" onkeyup="emailChk(this)"> <font
					id='emailValid'></font>
			</div>

			<button class="btn btn-custom w-100 mb-3">가입</button>
		</form>

		<div class="text-center">
			<a href="doLogin" class="btn-link-custom">로그인 화면으로 돌아가기</a>
		</div>

	</div>

	<script
		src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js"></script>

</body>

script 부분

	<script type="text/javascript">
  

    function win_upload(){
    	let op = "width=500,height=500 ,top=50 ,left=150";
    	open("registerImg","",op);
    	
    }
    
    
   function passwordChk(p){
    	const passVal = document.querySelector("#passValid");
    	if(!valid(p.value,'pass')){
    		passVal.innerHTML= '특수문자,영어,숫자포함 8~16자리';
    		passVal.style.color='red';
    	}
    	else{
    		passVal.innerHTML= '유효한비밀번호';
    		passVal.style.color='green';
    	}
    }
    
    function re_passwordChk(cp){ //비밀번호와 재입력한비밀번호가 같은지?
		let  p = document.querySelector("#password").value;
		let  pEqulasCp = document.querySelector("#pEqulasCp");
		if(!(p===cp.value)){
			pEqulasCp.innerHTML = '비밀번호가 일치하지않아요';
		}
		else{
			pEqulasCp.innerHTML = '';
		}

    }
    
    function phoneChk(t){
    	const phoneVal = document.querySelector("#phoneValid");
    	if(!valid(t.value,'phone')){
    		phoneVal.innerHTML= '올바른 휴대폰번호입력바람';
    		phoneVal.style.color='red';
    	}
    	else{
    		phoneVal.innerHTML= '유효한 번호';
    		phoneVal.style.color='green';
    	}
    }
    function emailChk(e){
    	const emailVal = document.querySelector("#emailValid");
    	if(!valid(e.value,'email')){
    		emailVal.innerHTML= '올바른 Email형식작성하세요';
    		emailVal.style.color='red';
    	}
    	else{
    		emailVal.innerHTML= '유효한E-mail';
    		emailVal.style.color='green';
    	}
    }
    
    
    
    //검증부분
    function valid(text,type){
    	if(type==='email'){//넘어온값과 name=email의 값이 동일할때
    		const regex = /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9._]+\.[a-zA-Z]{2,}$/;
    		return regex.test(text);
    	}
    	else if(type==='phone'){ //넘어온값과 name=tel의 값이 동일할때
    		const regex = /^(01[0126789])[ -]?\d{3,4}[ -]?\d{4}$/;
    		return regex.test(text);
    	}
    	else if(type==='pass'){ //비밀번호유효성검사
    		const regex = /^(?=.*[A-Za-z])(?=.*\d)(?=.*[\W_])[A-Za-z\d\W_]{8,16}$/;
    		//(?=.*[A-Za-z]) → 문자열 어딘가에 영문자가 있어야 해 (확인만)
    		//\W : 특수문자 , [A-Za-z\d\W_]{8,16} : 해당문자들이 8개~16개존재해야함
    		return regex.test(text);
    	}
    }
 
    
    //폼검증
    function input_check(f){
    	//f : <form...>
    	//f.pass : <input name="id">name이 pass인태그
    	
    	if(f.password.value.trim() == ""){ 
		alert("비밀번호입력")
		f.password.focus();
		return false; 
		}
    	else if(f.confirmPassword.value.trim() == ""){ 
		alert("비밀번호재입력")
		f.confirmPassword.focus();
		return false; 
	}
    	else if (f.password.value.trim() != f.confirmPassword.value.trim()) {
        alert("비밀번호와 재입력한 비밀번호가 일치하지 않습니다.");
        f.confirmPassword.focus();
        return false;
        }
    	else if(f.name.value.trim() == ""){ 
		alert("이름입력")
		f.name.focus();
		return false; 
	}
    	else if(f.birth.value.trim() == ""){
			alert("생년월일입력바람");

			return false;
    	}
    	else if(f.major.value.trim() == "none"){ 
		alert("전공선택")
		f.major.focus();
		return false; 
	}

    	else if(f.phone.value.trim() == ""){ 
		alert("전화번호입력바람")
		f.phone.focus();
		return false; 
	}
    	else if(f.email.value.trim() == ""){ 
		alert("email입력바람")
		f.email.focus();
		return false; 
	}
    	else if(!(
    	valid(f.password.value.trim(),'pass') 
		&& valid(f.email.value.trim(),'email')
		&& valid(f.phone.value.trim(),'phone')
			)){ //3개중 한개라도 유효성검사를 실패했다면 실행
			alert("형식을준수해주세요")
			return false;
		}
    	else{
			return true;
	}

    
    }
    
    </script>

 

코드를 보면 대충 감이오겠지만

전화번호의 형식 01x  xxxx  xxxx 의 형식이 아니면 폼을 전달할수없게끔 막아놨고

이메일도 이메일형식에맞춰서 해야 하며 , 

비밀번호는 특수문자와영문숫자를 모두포함하며 8자리~16자리 사이로만 입력이가능하게해놨다.

 

또한

비밀번호와 재입력비번도 같아야만 폼이넘어감

 


1-1) 가입버튼을 누르면 넘어가는 폼 컨트롤러 (registerNumChk ) + Bcrypt(암호화)

 

mypageController.java

registerNumChk 매핑부분

//회원가입버튼을 누르면 동작하게됨 ( 인증번호를 넘겨 인증번호가맞아야회원가입이완료됨)
	@RequestMapping("registerNumChk")
	public String  registerUserChk(HttpServletRequest request , HttpServletResponse response) throws ParseException {
		String name  = request.getParameter("name");
		String date = request.getParameter("birth");
		
		String pass = request.getParameter("password");
		String hashpw = BCrypt.hashpw(pass, BCrypt.gensalt());//hashPassword : 암호화 (복호화는불가능)
		
		String position = request.getParameter("position");
		String img = request.getParameter("picture");
		String deptId = request.getParameter("deptId");
		String email = request.getParameter("email");
		String phone = request.getParameter("phone");
		String id = MypageUsingMethod.IdChk(position);//직급에따른 아이디부여해주는 메서드(MypageUsingMethod에존재)


		SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
		Date birthDate = sdf.parse(date); // "YYYY-MM-dd" 형식의 문자열을 Date로 파싱

		//객체에 값 넣어주는과정 (교수 , 학생 따로)
		//직급 = 교수일경우
		if(id.contains("p")) {
			Professor pro = new Professor();
			pro.setProfessorId(id);
			pro.setProfessorImg(img);
			pro.setProfessorName(name);
			pro.setProfessorBirthday(birthDate);
			pro.setProfessorEmail(email);
			pro.setProfessorPassword(hashpw);
			//pro.setProfessorPassword(pass);
			pro.setDeptId(deptId);
			pro.setProfessorPhone(phone);
			System.out.println(pro);
			request.setAttribute("id", id);
			request.getSession().setAttribute("mem", pro);
			String num = EmailUtil.sendNum(email, name, id);
			request.setAttribute("num", num);
			System.out.println("인증번호 : "+num);

		}
		//학생일경우
		else {
			Student stu = new Student();
			stu.setStudentId(id);
			stu.setStudentNum(id.substring(1));
			stu.setDeptId(deptId);
			stu.setStudentName(name);
			stu.setStudentBirthday(birthDate);
			stu.setStudentEmail(email);
			stu.setStudentImg(img);
			stu.setStudentPassword(hashpw);
			//stu.setStudentPassword(pass);
			stu.setStudentPhone(deptId);
			stu.setStudentPhone(phone);
			stu.setStudentStatus("재학");

			System.out.println(stu);
			request.setAttribute("id", id);
			request.getSession().setAttribute("mem", stu);
			String num = EmailUtil.sendNum(email, name, id);
			request.setAttribute("num", num);
			System.out.println("인증번호 : "+num);
		}


		return "mypage/registerNumChk";
	}

String pass = Bcrypt.hashpw('비밀번호',Bcrypt.gensalt())를 이용하면

복호화가 불가능한 hashPassword가 생성됨

 

물론 비교하려면 Bcrypt.checkPw('입력값','비교할값(암호화된값)')으로 비교가능

 

입력한정보를 학생,교수를 구분해 객체(StudentDto  or ProfessorDto)에 모두 넣음.

 

생성한 객체(학생or교수)  , 생성한 인증번호 , id  를 세션으로등록해

registerNumChk로 보내준다

 


1-2) EmailUtil.sendNum() 메서드

javax.mail-1.6.2.jar
0.63MB

 

activation-1.1.1.jar
0.07MB

 

해당 라이브러리가있어야함

//registerUser 시 인증번호를 이메일로보내는메서드
	public static String sendNum(String toEmail, String userName, String id) {
        String host = "smtp.gmail.com";
        String from = "자신의gmail주소입력"; // 실제 Gmail 주소
        String password = "앱비밀번호입력"; // Gmail 앱 비밀번호
        String tempNum="";
        for (int i = 0; i < 4; i++) {
        	 int num = new Random().nextInt(9)+1;
        	 tempNum += num;
		}

        // SMTP 서버 속성 설정
        Properties props = new Properties();
        props.put("mail.smtp.host", host);
        props.put("mail.smtp.port", "587");
        props.put("mail.smtp.auth", "true");
        props.put("mail.smtp.starttls.enable", "true");
        props.put("mail.smtp.starttls.required", "true");
        props.put("mail.smtp.ssl.trust", "smtp.gmail.com");
        props.put("mail.transport.protocol", "smtp");
        props.put("mail.debug", "true"); // 디버깅 활성화


        // 세션 생성
        Session session = Session.getInstance(props, new Authenticator() {
            @Override
            protected PasswordAuthentication getPasswordAuthentication() {
                return new PasswordAuthentication(from, password);
            }
        });

        try {
            MimeMessage message = new MimeMessage(session);
            message.setFrom(new InternetAddress(from));
            message.addRecipient(Message.RecipientType.TO, new InternetAddress(toEmail));
            message.setSubject("인증번호 안내");
            message.setText(userName+"님의 인증번호 : "+tempNum+"\n\n -LDB 학사관리부-");
            

            Transport.send(message);
            System.out.println("이메일 전송 성공: " + toEmail);
            return tempNum;
           

        } catch (MessagingException e) {
            e.printStackTrace();
            System.out.println("이메일 전송 실패: " + e.getMessage());
        }
        return null;
       
    }
 String tempNum="";
        for (int i = 0; i < 4; i++) {
        	 int num = new Random().nextInt(9)+1;
        	 tempNum += num;
		}

다음과같은방식으로 1~9 사이의 숫자를 랜덤하게 뽑아내서

4자리숫자를 만들어

 

랜덤한 4자리숫자를 파라미터로 넘겨받은 이메일로 보내게될거임


1-3) 인증번호를 입력해 검증하는곳 registerNumChk (성공시 회원가입이될거임)

<body>
    <div class="card">
        <h4 class="text-center mb-4">인증번호 입력</h4>
        <form action="registerSuccess" onsubmit="return numChk(this)">      
        <div class="mb-3">
            <label for="name" class="form-label">인증번호</label>
            <input type="text" class="form-control" id="num" name="num" placeholder="인증번호입력">
            <input type="hidden" value="${mem}" name="mem">
            <input type="hidden" value="${id}" name="id">
            <input type="hidden" value="${num}" name="emailNum">
        </div>
        <button class="btn btn-custom w-100 mb-3">인증</button>
        </form>
    </div>
    <script type="text/javascript">
    	function numChk(f){
			if(f.num.value.trim() !== f.emailNum.value){
				alert("인증번호가틀려요")
				return false
			}
			else{
				return true;
			}
    	}
    </script>

    <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js"></script>
</body>

인증번호를 입력하는 칸이있음

아까 컨트롤러에서 넘겨준 인증번호(num)과 입력한 인증번호(num)이 같다면

registerSuccess로 넘어가게됨

(객체,id,num이 파라미터로넘어갈거임)


1-4) 회원가입 성공(registerSuccess) 

 

//회원가입 인증번호가 정상적으로 입력되었다면 넘어오는 곳 (페이지는없음)
	@RequestMapping("registerSuccess")
	public String  registerSuccess(HttpServletRequest request , HttpServletResponse response) throws ParseException {
		String id = request.getParameter("id");
		String msg = "회원가입성공 id = "+id;
		String url = "doLogin";
		
		
		if(id.toLowerCase().contains("s")) {
			Student stu = (Student)request.getSession().getAttribute("mem");
			StudentDao sDao = new StudentDao();
			if(!sDao.insert(stu)) { //DB에오류발생시
				msg = "회원가입실패";
				url = "registerUser";
			}
			else {
				//회원가입성공 시 해당email로 발급된 id를 보내줄거임
				String email = stu.getStudentEmail();
				String name = stu.getStudentName();
				EmailUtil.sendIdEmail(email, name, id);
				request.setAttribute("msg", msg);
				request.setAttribute("url", url);//doLogin
			}
		}
		else {
			Professor pro = (Professor)request.getSession().getAttribute("mem");
			ProfessorDao pDao = new ProfessorDao();
			if(!pDao.insert(pro)) {
				msg = "회원가입실패";
				url = "registerUser";
			}
			else {
				String email = pro.getProfessorEmail();
				String name = pro.getProfessorName();
				EmailUtil.sendIdEmail(email, name, id);
				request.setAttribute("msg", msg);
				request.setAttribute("url", url);

			}

		}
		 //회원가입이실패하든 성공하든 세션정보는 모두지워줌
		request.getSession().invalidate();
		return "alert";
	}

성공시 id를 창으로 띄워주긴하지만

검증이 완료된 이메일로 id를 보내준다


2)pw찾기

아이디와 이메일을입력 후 비밀번호찾기를 클릭시

findPwProcess폼으로 넘어감

2-1) findPwProcess , getTempPw(임시비번생성알고리즘)

//임시비밀번호를 만드는 알고리즘(비밀번호찾기 시에만 발급이 될것임)
		public  String getTempPw() {
			List<String> list = Arrays.asList
					("a" ,"b" ,"c" ,"d" ," e" ,"f" ,"g" ,"h" ,"i" ,"j" ,"k" ,"l" ,"m" ,"n" ,"o" ,"p","q","r","s","t" );
					
					List<String> list2 = new ArrayList<>();
					for (String string : list) {
						list2.add(string.toUpperCase());
					}	

					List<Object> combineList = new ArrayList<>();
					combineList.addAll(list);
					combineList.addAll(list2);
					for (int i = 0; i < 15; i++) { //랜덤한0~9 숫자 15개집어넣기
						 combineList.add(new Random().nextInt(10)); 
					}
					Collections.shuffle(combineList);
					String tempNum = "";
					for (int i = 0; i < 6; i++) {
						int num = new Random().nextInt(combineList.size());
						tempNum += combineList.get(num);
					}
					System.out.println(tempNum);
					return tempNum;
				}

 

실행할때마다 랜덤한6자리의

숫자,영문 조합 임시비밀번호발급

 

	@RequestMapping("findPwProcess")
	public String findPwProcess(HttpServletRequest request, HttpServletResponse response) {
		String id = request.getParameter("id");
		String email = request.getParameter("email");
		String pw = new ProStuDao().findPw(id,email);//아이디와이메일이 일치 시 비밀번호를 반환
		if(pw==null) {
			request.setAttribute("msg", "입력된정보가없어요");			
			return "mypage/close";
		}
		else {
			String tempPw = getTempPw(); //임시비번생성하는 알고리즘이있는 메서드
			String hashpw = BCrypt.hashpw(tempPw, BCrypt.gensalt()); //임시비밀번호를 암호화
			
			if(new ProStuDao().updateTempPw(hashpw,id)) { //넘겨받은 id의 비밀번호를 임시비번으로 업데이트
				EmailUtil.sendTempPw(email, id, tempPw);//임시비밀번호를 메일로발송
				request.setAttribute("msg", "임시 비밀번호는"+tempPw+"입니다"); 
				request.setAttribute("id", id);
				request.setAttribute("email", email);

				//알림창을 띄워주고 pwUpdate폼으로이동
				return "mypage/alertPw";
			}
			else {
				request.setAttribute("msg", "오류발생");
				return "mypage/close";
			}


		}
	}

아이디,이메일이 일치 시

pw를 알려주고싶지만복호화를 못하게 막아놨기때문에

임시비밀번호를 발급해 업데이트를 실시

 

alert창으로 임시비밀번호를알려주고 , 해당이메일로도 임시비밀번호를 보내줌.

 

 

alert창을 닫으면 비밀번호변경폼으로 자동으로 넘어가게 해놨음

(나중에 바꿔도상관은없다)

 

(현재비밀번호는 임시비밀번호가 들어갈수도있기때문에 유효성검사를 제외시킴)