반응형
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() 메서드
해당 라이브러리가있어야함
//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창을 닫으면 비밀번호변경폼으로 자동으로 넘어가게 해놨음
(나중에 바꿔도상관은없다)
(현재비밀번호는 임시비밀번호가 들어갈수도있기때문에 유효성검사를 제외시킴)
'프로젝트' 카테고리의 다른 글
개인정보(userInfo)퇴학 , 성적확인 , 임시비밀번호알고리즘변경 (3) | 2025.05.19 |
---|---|
프로젝트중간 (개인정보(userInfo) 내용들 , index매핑과정 controller변경) (0) | 2025.05.14 |
프로젝트진행중 ( 로그인시 index에 정보몇개전달 , id/pw찾기) (0) | 2025.05.13 |
프로젝트진행중 - 회원가입 , 로그인? (0) | 2025.05.12 |
주말에 프로젝트조금해보기(회원가입만 해보자...!) (1) | 2025.05.10 |