회원가입할때 이메일을 통해 본인인증을 하고 인증된 경우에만 회원가입이 가능하도록하는 기능을 구현하기로 하였다.
우선 회원가입시 입력한 이메일주소로 인증 코드를 발송하여 해당 코드와 회원가입하려는 사용자가 입력한 코드가 일치한지를 확인하는 작업이 수행되어야 한다.
이메일 인증을 위해서 이메일 전송 기능 부터 생성했다.
이메일 전송 기능을 사용하기 위해 google 애플리케이션 비밀번호를 발급받아야한다.
https://myaccount.google.com/security
Google 계정
myaccount.google.com
이후에 본인의 gmail 주소와 발급받은 키를 "" 안에 차례로 작성하여 준다.
package com.example.board.email;
import javax.mail.Authenticator;
import javax.mail.PasswordAuthentication;
public class SMTPAuthenticator extends Authenticator {
@Override
protected PasswordAuthentication getPasswordAuthentication() {
return new PasswordAuthentication(
"", "");
}
}
그리고 이메일 전송하는 작업을 위한 class 를 작성하였다.
컨트롤러를 통해 sendMail을 불러 실행하면 이메일이 전송되었다.
package com.example.test.model;
import java.util.Properties;
import javax.mail.Address;
import javax.mail.Message;
import javax.mail.Session;
import javax.mail.Transport;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeMessage;
public class Mailer {
public void sendMail(
String to, String subject, String content, SMTPAuthenticator smtp) {
Properties p = new Properties();
p.put("mail.smtp.host", "smtp.gmail.com");
p.put("mail.smtp.port", "465");
p.put("mail.smtp.starttls.enable", "true");
p.put("mail.smtp.auth", "true");
p.put("mail.smtp.debug", "true");
p.put("mail.smtp.socketFactory.port", "465");
p.put("mail.smtp.socketFactory.class", "javax.net.ssl.SSLSocketFactory");
p.put("mail.smtp.socketFactory.fallback", "false");
try {
Session ses = Session.getInstance(p, smtp);
ses.setDebug(true);
MimeMessage msg = new MimeMessage(ses); // 메일의 내용을 담을 객체
msg.setSubject(subject); // 제목
Address fromAddr = new InternetAddress("xxxx@gmail.com");
msg.setFrom(fromAddr); // 보내는 사람
Address toAddr = new InternetAddress("xxxx@naver.com");
msg.addRecipient(Message.RecipientType.TO, toAddr); // 받는 사람
msg.setContent(content, "text/html;charset=UTF-8"); // 내용과 인코딩
Transport.send(msg); // 전송
} catch (Exception e) {
System.out.println("실패");
e.printStackTrace();
}
}
}
회원가입 페이지에서 사용자가 작성한 이메일주소를 가져오고 그 주소로 랜덤코드를 전송하는 것
랜덤코드를 session에 저장하는것 ( session에 저장하면 보안상 좋지는 않음 )
저장되어있는 랜덤코드와 사용자가 작성한 인증 코드를 비교하여 일치 불일치 다르게 나타나게하기
위 세가지를 Modal창에서 수행할수 있도록 구현하였다.
1.
package com.example.board.email;
import java.util.Properties;
import java.util.Random;
import javax.servlet.http.HttpSession;
import javax.mail.Address;
import javax.mail.Message;
import javax.mail.Session;
import javax.mail.Transport;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeMessage;
import javax.servlet.http.HttpSession;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
@Controller
public class Mailer {
@Autowired
HttpSession session;
@GetMapping("/emailcode")
@ResponseBody
public void sendMail(
SMTPAuthenticator smtp , @RequestParam String email){
String content = Long.toString(Math.abs(new Random().nextLong()),36).substring(0,4);
Properties p = new Properties();
p.put("mail.smtp.host", "smtp.gmail.com");
p.put("mail.smtp.port", "465");
p.put("mail.smtp.starttls.enable", "true");
p.put("mail.smtp.auth", "true");
p.put("mail.smtp.debug", "true");
p.put("mail.smtp.socketFactory.port", "465");
p.put("mail.smtp.socketFactory.class", "javax.net.ssl.SSLSocketFactory");
p.put("mail.smtp.socketFactory.fallback", "false");
try {
Session ses = Session.getInstance(p, smtp);
ses.setDebug(true);
MimeMessage msg = new MimeMessage(ses); // 메일의 내용을 담을 객체
msg.setSubject("pika충전소 회원가입 본인 인증"); // 제목
Address fromAddr = new InternetAddress("xxxx@gmail.com");
msg.setFrom(fromAddr); // 보내는 사람
Address toAddr = new InternetAddress(email);
msg.addRecipient(Message.RecipientType.TO, toAddr); // 받는 사람
msg.setContent(content, "text/html;charset=UTF-8"); // 내용과 인코딩
Transport.send(msg); // 전송
} catch (Exception e) {
e.printStackTrace();
}
session.setAttribute("code",content);
}
@GetMapping("emailcode-check")
@ResponseBody
public String codecheck(@RequestParam String emailcode){
Object code = session.getAttribute("code");
System.out.println(code);
System.out.println(emailcode);
if(code.equals(emailcode) ){
System.out.println("yes");
return "코드일치";
}else{
System.out.println("no");
return "코드불일치";
}
}
}
2.
<!-- Modal -->
<div id="myModal" class="modal">
<div class="modal-dailog">
<div class="modal-content" style="color:black;">
<span class="close">×</span>
<div class ="subscribe-title">이메일 인증 코드 입력</div>
<div class="subscribe-content">
<div class="content-info">
<input type="text" class ="codetext">
</div>
<div class ="coupon-btn" style="display: flex; justify-content: space-evenly;align-items: center; margin-top: 20px;">
<a href="#" class ="btn-gradient yellow large" onclick="checkEmailcode()" >확인</a>
</div>
</div>
</div>
</div>
</div>
<script>
const signupButton = document.getElementById('signup');
if (duplicateResultElement.textContent === "사용 가능한 Email입니다." && contentInfo.innerHTML === "<h1>성공</h1>") {
signupButton.disabled = false;
} else {
signupButton.disabled = true;
}
//이메일 인증 모달창
async function run(code){
const options = {backdrop:false};
var myModal = new bootstrap.Modal(document.getElementById('myModal'), options);
myModal.show();
const email = document.querySelector("[name=email]").value;
const response = await fetch(`/emailcode?email=${email}`);
$('.close').click(function(){
$('#myModal').css('display','none');
$('.subscribe-title').empty();
});
}
// 이메일 인증
async function checkEmailcode(){
const emailcode = document.querySelector('.codetext').value;
const response = await fetch(`/emailcode-check?emailcode=${emailcode}`);
const result = await response.text();
console.log(response);
console.log(result);
if (result === "코드불일치") {
var contentInfo = document.querySelector('.content-info');
contentInfo.innerHTML = '<h1>코드불일치</h1>';
} else if (result === "코드일치") {
var contentInfo = document.querySelector('.content-info');
contentInfo.innerHTML = '<h1>성공</h1>';
}
첫번째는 두개의 주소를 사용한 controller 코드이다.
각각 요청과 응답에 맞는 주소가 있어야한다고 한다.
이를 몰라서 한 주소에 동시에 두가지 요청과 응답이 가능하게 구현하려다가 실패했다.
두번째는 html 코드이다.
Modal 창을 띄우고 이에 코드를 입력할 수 있도록 input text를 넣었다.
function run() 은 Modal 창을 띄우고 이메일 주소를 포함하여 서버로 보내는 함수이다.
이때 받은 이메일 주소로 서버에서 인증코드를 랜덤으로 생성하여 sendMail 에서 이메일을 전송한 후 랜덤 생성된 코드를 session에 저장한다.
function checkEmailcode() 은 Modal 창의 사용자가 입력한 코드를 실제 발송한 코드와 비교하기 위해 코드를 포함하여 서버로 보내고 서버에서 이전에 저장한 랜덤 코드와 동일한지 검사 후 일치하면 "코드일치" 일치하지 않으면 "코드불일치"를 return 한다.
이때 내가 실수한 부분이 @ResponseBody를 작성하지 않았다는 것이다. 이를 작성하지않으면 자꾸 "코드불일치"라는 html 이 존재하지 않는다는 오류가 나온다.
또한 일치 불일치 검사할때 단순하게 code != emailcode 로 조건을 주었는데 '==' 연산자는 객체의 참조를 비교하는 연산자라 객체의 내용을 비교하지 않기때문에 객체가 다른 code와 emailcode의 내용을 비교하기 위해서는 'equals()'를 사용해야한다. 이를 몰라서 자꾸만 코드 불일치로 결과값이 나와서 system.out.println()을 사용하여 code 와 emailcode를 비교하는 작업을 수행하느라 시간이 걸렸다.
이메일 인증 버튼을 클릭하면 위와 같은 Modal이 생성되고 인증코드를 틀리면
위와 같이 코드 불일치라고 뜬다.
이메일 인증 기능을 구현하며 원래 있었던 아이디 중복 검사와 이메일 인증 검사를 통과하여야 가입하기 버튼이 활성화가되는 코드도 구현하였다. 두번째 코드에 있다.
처음에는 count를 0으로 선언하고 아이디 중복 검사와 이메일 인증검사 각각 성공했을때에 count+=1 로 count가 2가 되었을때만 활성화되게 하려고 했으나 오류가 발생하였다.
동 님의 아이디어로 위와 같이 성공했을때 출력되는 값을 통해 구현할 수 있었다.
'웹 개발' 카테고리의 다른 글
카카오 지도 api 활용 - 카테고리에 따른 마커 생성 (6) | 2023.10.25 |
---|---|
카카오 지도 api 활용 - 현재 위치 기준 반경 데이터만 마커 생성2 (1) | 2023.10.25 |
Modal 창 구현2 (1) | 2023.10.25 |
Modal 창 구현 (1) | 2023.10.24 |
Excel 데이터를 h2 데이터베이스에 저장하기 (0) | 2023.10.24 |