국비 86 - 이메일 인증(스프링)

냐아암·2023년 8월 21일
0

국비

목록 보기
99/114

🔑 구글 기준!

2단계 인증 후 앱 비밀번호를 발급받아야 한다

<!-- 이메일 입력 -->
                <label for="memberEmail">
                    <span class="required">*</span> 아이디(이메일)
                </label>


                <div class="signUp-input-area">
                    <input type="text" name="memberEmail" id="memberEmail"
                    placeholder="아이디(이메일)" maxlength="30" autocomplete="off">
                   
                    <button id="sendAuthKeyBtn" type="button">인증번호 받기</button>
                </div>
                <span class="signUp-message" id="emailMessage">메일을 받을 수 있는 이메일을 입력해주세요.</span>






                <!-- 인증번호 입력 -->
                <label for="emailCheck">
                    <span class="required">*</span> 인증번호
                </label>


                <div class="signUp-input-area">
                    <input type="text" name="authKey" id="authKey" s placeholder="인증번호 입력" maxlength="6" autocomplete="off" >
                   
                    <button id="checkAuthKeyBtn" type="button">인증하기</button>
                </div>
                <span class="signUp-message" id="authKeyMessage"></span>
                                <!-- 인증번호가 일치하지 않습니다 -->

xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

	<!-- email 인증 관련 bean 생성 -->
	<bean id="mailSender"
		class="org.springframework.mail.javamail.JavaMailSenderImpl">
		<property name="host" value="smtp.gmail.com" />
		<property name="port" value="587" />
		<property name="username" value="이메일" />
		<property name="password" value="앱 비밀번호" />
		<property name="javaMailProperties">
			<props>
				<prop key="mail.transport.protocol">smtp</prop>
				<prop key="mail.smtp.auth">true</prop>
				<prop key="mail.smtp.starttls.enable">true</prop>
				<!-- <prop key="mail.debug">true</prop> -->
				<prop key="mail.smtp.ssl.trust">smtp.gmail.com</prop>
				<prop key="mail.smtp.ssl.protocols">TLSv1.2</prop>
			</props>
		</property>
	</bean>
</beans>

// 이메일 인증

// 인증번호 발송
const sendAuthKeyBtn = document.getElementById("sendAuthKeyBtn"); // 인증번호 받기 버튼
const authKeyMessage = document.getElementById("authKeyMessage"); //인증번호가 일치하지 않습니다 문구 부분
let authTimer;
let authMin = 4;
let authSec = 59;

// 인증번호를 발송한 이메일 저장
let tempEmail;

sendAuthKeyBtn.addEventListener("click", function(){
    authMin = 4;
    authSec = 59; // 4분 59초로 세팅

    checkObj.authKey = false;

    if(checkObj.memberEmail){ // 중복이 아닌 이메일인 경우

        /* fetch() API 방식 ajax */
        fetch("/sendEmail/signUp?email="+memberEmail.value)
        .then(resp => resp.text())
        .then(result => {
            if(result > 0){
                console.log("인증 번호가 발송되었습니다.")
                tempEmail = memberEmail.value;
            }else{
                console.log("인증번호 발송 실패")
            }
        })
        .catch(err => {
            console.log("이메일 발송 중 에러 발생");
            console.log(err);
        });
       
        alert("인증번호가 발송 되었습니다.");
       
        authKeyMessage.innerText = "05:00";
        authKeyMessage.classList.remove("confirm");

        authTimer = window.setInterval(()=>{

            authKeyMessage.innerText = "0" + authMin + ":" + (authSec<10 ? "0" + authSec : authSec);
           
            // 남은 시간이 0분 0초인 경우
            if(authMin == 0 && authSec == 0){
                checkObj.authKey = false;
                clearInterval(authTimer);
                return;
            }

            // 0초인 경우
            if(authSec == 0){
                authSec = 60;
                authMin--;
            }

            authSec--; // 1초 감소

        }, 1000)

    } else{
        alert("중복되지 않은 이메일을 작성해주세요.");
        memberEmail.focus();
    }

});


// 인증 확인
const authKey = document.getElementById("authKey"); // 인증번호 입력칸
const checkAuthKeyBtn = document.getElementById("checkAuthKeyBtn"); // 인증번호 확인 버튼

checkAuthKeyBtn.addEventListener("click", function(){

    if(authMin > 0 || authSec > 0){ // 시간 제한이 지나지 않은 경우에만 인증번호 검사 진행
        /* fetch API */
        const obj = {"inputKey":authKey.value, "email":tempEmail}
        const query = new URLSearchParams(obj).toString() // 파라미터가 많을 때 K:V로 작성하기 번거로움 -> 이 방법 사용 
        // inputKey=123456&email=user01
        
        fetch("/sendEmail/checkAuthKey?" + query)
        .then(resp => resp.text())
        .then(result => {
            if(result > 0){
                clearInterval(authTimer);
                authKeyMessage.innerText = "인증되었습니다.";
                authKeyMessage.classList.add("confirm");
                checkObj.authKey = true;

            } else{
                alert("인증번호가 일치하지 않습니다.")
                checkObj.authKey = false;
            }
        })
        .catch(err => console.log(err));

    } else{
        alert("인증 시간이 만료되었습니다. 다시 시도해주세요.")
    }

});

Controller

package edu.kh.project.member.controller;


import java.util.Map;


import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.SessionAttributes;


import edu.kh.project.member.model.service.EmailService;


@Controller
@RequestMapping("/sendEmail")
@SessionAttributes("authKey")
public class EmailController {

	@Autowired
	private EmailService service;

	@GetMapping("/signUp")
	@ResponseBody
	public int signUp(String email) {
		return service.signUp(email, "회원 가입");
	}


}

Service

\package edu.kh.project.member.model.service;


import java.util.Map;


public interface EmailService {

	int signUp(String email, String title);

	String createAuthKey();

	int checkAuthKey(Map<String, Object> paramMap);
}

ServiceImpl

package edu.kh.project.member.model.service;


import java.util.HashMap;
import java.util.Map;


import javax.mail.Message;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeMessage;


import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.mail.javamail.JavaMailSender;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;


import edu.kh.project.member.model.dao.EmailDAO;


@Service
public class EmailServiceImpl implements EmailService {


	@Autowired
	private EmailDAO dao;

	@Autowired
	private JavaMailSender mailSender;

	private String fromEmail = "이메일";
	private String fromUsername = "수업용프로젝트";

	@Override
	public String createAuthKey() {
		String key = "";
		for(int i=0 ; i< 6 ; i++) {

			int sel1 = (int)(Math.random() * 3); // 0:숫자 / 1,2:영어

			if(sel1 == 0) {

				int num = (int)(Math.random() * 10); // 0~9
				key += num;

			}else {

				char ch = (char)(Math.random() * 26 + 65); // A~Z

				int sel2 = (int)(Math.random() * 2); // 0:소문자 / 1:대문자

				if(sel2 == 0) {
					ch = (char)(ch + ('a' - 'A')); // 소문자로 변경
				}

				key += ch;
			}

		}
		return key;
	}


	@Transactional
	@Override
	public int signUp(String email, String title) {

		//6자리 난수 인증번호 생성
		String authKey = createAuthKey();
		try {


			//인증메일 보내기
			MimeMessage mail = mailSender.createMimeMessage();

			// 제목
			String subject = "[Board Project]"+title+" 인증코드";

			// 문자 인코딩
			String charset = "UTF-8";

			// 메일 내용
			String mailContent 
			= "<p>Board Project "+title+" 인증코드입니다.</p>"
					+ "<h3 style='color:blue'>" + authKey + "</h3>";



			// 송신자(보내는 사람) 지정
			mail.setFrom(new InternetAddress(fromEmail, fromUsername));
			mail.addRecipient(Message.RecipientType.TO, new InternetAddress(email));

			// 수신자(받는사람) 지정

			// 이메일 제목 세팅
			mail.setSubject(subject, charset);

			// 내용 세팅
			mail.setText(mailContent, charset, "html"); //"html" 추가 시 HTML 태그가 해석됨

			mailSender.send(mail);
		} catch (Exception e) {
			e.printStackTrace();
			return 0;
		}

		Map<String, String> map = new HashMap<String, String>();
		map.put("authKey", authKey);
		map.put("email", email);

		System.out.println(map); //{inputKey=xNsH0Q, email=khj981008@naver.com}

		int result = dao.updateAuthKey(map);

		if(result == 0) {
			result = dao.insertAuthKey(map);
		}



		return result;
	}

}

DAO

@Repository
public class EmailDAO {

	@Autowired
	private SqlSessionTemplate sqlSession;

	public int updateAuthKey(Map<String, String> map) {
		return sqlSession.update("emailMapper.updateAuthKey", map);
	}

sql

<insert id="insertAuthKey">
		INSERT INTO "AUTH_KEY" VALUES(SEQ_AUTH_KEY_NO.NEXTVAL, #{authKey}, #{email},
		DEFAULT)
	</insert>

DAO

public int insertAuthKey(Map<String, String> map) {
		return sqlSession.update("emailMapper.insertAuthKey", map);
	}

sql

<update id="updateAuthKey">
		UPDATE "AUTH_KEY" SET
		CODE = #{authKey},
		CREATE_TIME = sysdate
		WHERE EMAIL = #{email}
	</update>

Controller

@GetMapping("/checkAuthKey")
	@ResponseBody
	public int checkAuthKey(@RequestParam Map<String, Object> paramMap){


		System.out.println(paramMap); // {inputKey=wc3rxG, email=knbdh@nate.com}

		return service.checkAuthKey(paramMap);
	}

Service

package edu.kh.project.member.model.service;


import java.util.Map;


public interface EmailService {

	int signUp(String email, String title);

	String createAuthKey();

	int checkAuthKey(Map<String, Object> paramMap);
}

ServiceImpl

@Override
	public int checkAuthKey(Map<String, Object> paramMap) {
		return dao.checkAuthKey(paramMap);
	}  

DAO

public int checkAuthKey(Map<String, Object> paramMap) {
		return sqlSession.selectOne("emailMapper.checkAuthKey", paramMap);
	}

sql

<select id="checkAuthKey" resultType="int">
		SELECT COUNT(*) FROM "AUTH_KEY"
		WHERE EMAIL = #{email}
		AND CODE = #{inputKey}
	</select>
profile
개발 일지

0개의 댓글