Notice
Recent Posts
Recent Comments
Link
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | ||
6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 | 14 | 15 | 16 | 17 | 18 | 19 |
20 | 21 | 22 | 23 | 24 | 25 | 26 |
27 | 28 | 29 | 30 | 31 |
Tags
- iframe 태그
- sub태그
- sup태그
- not 의사클래스
- RGBA
- 전체 선택자
- 일반 형제 결합자
- RGB
- Live Server
- padding 속성
- 임베디드
- i 태그
- Checked 의사 클래스
- background-color 속성
- height속성
- 인접 형제 결합자
- tag html
- id 선택자
- html tag i
- css
- focus 의사클래스
- reveal in file explorer
- 자식결합자
- 아두이노
- width속성
- br 태그
- html
- iframe
- go live
- html 태그
Archives
- Today
- Total
so woon!
이메일 찾기 구현 본문
recoverEmail.html
<!doctype html>
<html lang="ko" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<meta name="viewport"
content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>또치뱅크 - 이메일 찾기</title>
<th:block th:replace="~{fragments/head :: common}"></th:block>
<script defer th:src="@{/member/resources/scripts/recoverEmail.js}"></script>
<link rel="stylesheet" th:href="@{/member/resources/stylesheets/recoverEmail.css}">
</head>
<body>
<th:block th:replace="~{fragments/body :: header}"></th:block>
<th:block th:replace="~{fragments/body :: cover}"></th:block>
<main class="--main main">
<form name="form" id="form">
<div class="title-container">
<h1 class="title">이메일 찾기</h1>
</div>
<table class="table">
<tbody>
<!--이름 입력창-->
<tr>
<th rowspan="1" id="name">이름</th>
<td>
<label class="label name">
<input autofocus class="--object-input input" maxlength="50" name="name"
placeholder="이름을 입력해 주세요." type="text">
</label>
</td>
</tr>
<!-- 연락처 입력창-->
<tr>
<th rowspan="1" id="contact">연락처</th>
<td>
<label class="label contact">
<span hidden>연락처</span>
<input autofocus class="--object-input input" maxlength="50" name="contact"
placeholder="연락처를 입력해 주세요." type="text">
</label>
</td>
</tr>
<!--이메일 찾기 버튼-->
<tr>
<th id="login"></th>
<td>
<label>
<input class="login-button" id="login-button" name="loginButton"
type="submit" value="이메일 찾기" href="#">
</label>
</td>
</tr>
<!--입력하신 이메일은 ~입니다.-->
<tr class="emailAlert" rel="emailAlert">
<th></th>
<td>
<span>
<span class="emailAlertText"></span>
</span>
</td>
</tr>
<!--로그인하러 가기-->
<tr class="warning-login">
<th></th>
<td>
<label class="warningLogin">
<input class="goLoginText" id="goLoginText" name="goLoginText"
type="submit" value="로그인하러 가기" onclick="location.href='login'">
</label>
</td>
</tr>
<!--경고창 - 이름 or 연락처를 입력해주세요-->
<tr class="warning-row" rel="warningRow">
<th></th>
<td>
<span class="warning">
<i class="icon fa-solid fa-triangle-exclamation"></i>
<span class="text"></span>
</span>
</td>
</tr>
</tbody>
</table>
</form>
</main>
<th:block th:replace="~{fragments/body :: footer}"></th:block>
</body>
</html>
recoverEmail.css
@charset "UTF-8";
/*큰 틀*/
#form {
width: 100%;
max-width: 40rem;
align-items: stretch;
display: flex;
flex-direction: column;
justify-content: flex-start;
margin: 5rem 2rem;
}
#form > .title-container {
margin-bottom: 3rem;
}
#form > .title-container > .title {
font-size: 2rem;
font-weight: 500;
}
#form > .table {
width: 100%;
max-width: 30rem;
align-self: center;
border: none;
border-collapse: collapse;
}
#form > .table th {
padding-right: 1rem;
white-space: nowrap;
}
#form > .table td {
width: 100%;
}
/*이메일 찾기 버튼*/
#form > .table > tbody > tr > td > label > #login-button {
background-color: rgb(40, 117, 225);
color: rgb(255, 255, 255);
width: 100%;
margin-top: 0.25rem;
padding: 1rem 1rem;
border-radius: 0.3rem;
}
/*경고창*/
#form > .table .warning-row {
display: none;
}
#form > .table .warning-row.visible {
display: table-row;
}
#form > .table .warning-row .warning {
background-color: rgb(231, 76, 60);
border-radius: 0.375rem;
color: rgb(255, 255, 255);
font-size: 1rem;
margin-top: 0.25rem;
padding: 1rem 1.25rem;
align-items: center;
display: flex;
flex-direction: row;
justify-content: flex-start;
}
#form > .table .warning-row .warning > .icon {
font-size: 1.5rem;
margin: 0 1.25rem 0 0.25rem;
}
#form > .table .warning-row .warning > .text {
flex: 1;
text-align: justify;
}
/* 입력하신 뭐시기*/
#form > .table > tbody > .emailAlert > td > span > .emailAlertText {
display: none;
}
#form > .table > tbody > .emailAlert > td > span > .emailAlertText.visible {
background-color: rgb(40, 117, 225);
color: rgb(255, 255, 255);
width: 100%;
margin-top: 0.25rem;
padding: 1rem 1rem;
border-radius: 0.3rem;
align-items: center;
display: flex;
flex-direction: row;
justify-content: center;
flex: 1;
text-align: justify;
}
/*로그인하러가기*/
#form > .table > tbody > tr > td > label > .goLoginText {
display: none;
}
#form > .table > tbody > tr > td > label > .goLoginText.visible {
background-color: rgb(40, 117, 225);
color: rgb(255, 255, 255);
width: 100%;
margin-top: 0.25rem;
padding: 1rem 1rem;
border-radius: 0.3rem;
align-items: center;
display: flex;
flex-direction: row;
justify-content: center;
flex: 1;
text-align: justify;
}
/* footer */
.--footer {
background-color: #191f28;
color: #b0b8c1;
}
recoverEmail.js
const form = window.document.getElementById('form');
const Warning = {
getElementById: () => form.querySelector('[rel="warningRow"]'),
show: (text) => {
const warningRow = Warning.getElementById();
warningRow.querySelector('.text').innerText = text;
warningRow.classList.add('visible');
},
hide: () => Warning.getElementById().classList.remove('visible')
};
let text = form.querySelector('.text');
let emailAlertText = form.querySelector('.emailAlertText');
let goLoginText = form.querySelector('.goLoginText');
form.onsubmit = (e) => {
e.preventDefault();
Warning.hide();
if (form['name'].value === '') {
form.querySelector('.warning-row').classList.add('visible');
text.innerText = '이름을 입력해 주세요.';
form['name'].focus();
return false;
}
if (form['contact'].value === '') {
form.querySelector('.warning-row').classList.add('visible');
text.innerText = '연락처를 입력해 주세요.';
form['contact'].focus();
return false;
}
const xhr = new XMLHttpRequest();
const formData = new FormData();
formData.append('name', form['name'].value);
formData.append('contact', form['contact'].value);
xhr.open('POST', '/member/recoverEmail');
xhr.onreadystatechange = () => {
if (xhr.readyState === XMLHttpRequest.DONE) {
if (xhr.status >= 200 && xhr.status < 300) {
const responseObject = JSON.parse(xhr.responseText);
switch (responseObject['result']) {
case 'success':
form.querySelector('.emailAlertText').classList.add('visible');
emailAlertText.innerText = '입력하신 이름과 연락처를 가진 회원의 이메일은\n ? \n입니다.';
form.querySelector('.goLoginText').classList.add('visible');
break;
default:
Warning.show('입력한 정보와 일치하는 회원이 없습니다.');
break;
}
} else {
Warning.show('서버와 통신하지 못하였습니다.\n잠시 후 다시 시도해 주세요.');
}
}
};
xhr.send(formData);
}
MemberControllor.java
package dev.xowoony.studymemberbbs.controllers;
import dev.xowoony.studymemberbbs.entities.member.EmailAuthEntity;
import dev.xowoony.studymemberbbs.entities.member.UserEntity;
import dev.xowoony.studymemberbbs.enums.CommonResult;
import dev.xowoony.studymemberbbs.interfaces.IResult;
import dev.xowoony.studymemberbbs.services.MemberService;
import org.json.JSONObject;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.servlet.ModelAndView;
import javax.mail.MessagingException;
import java.security.NoSuchAlgorithmException;
@Controller
// 컨트롤러임을 명시한다.
@RequestMapping(value = "/member")
// 맵핑, 주소값은 localhost:8080/member
public class MemberController {
// 컨트롤러와 서비스간 의존성을 주입한다.
private final MemberService memberService;
@Autowired
//요구되는 타입을 스프링부트가 알아서 객체화 하여 전달토록 한다. (컨트롤러-서비스 간) 의존성 주입을 위해 사용.
public MemberController(MemberService memberService) {
this.memberService = memberService;
}
// MemberController 클래스를 생성한다.
@RequestMapping(value = "register", method = RequestMethod.GET, produces = MediaType.TEXT_HTML_VALUE)
//맵핑, 주소값은 localhost:8080/member/register임, GET(주소창 입력) 방식으로 요청, 응답을 MediaType의 TEXT_HTML_VALUE로 돌려주겠다.
public ModelAndView getRegister() {
ModelAndView modelAndView = new ModelAndView("member/register");
//ModelAndView를 객체화 한다.
return modelAndView;
// modelAndView를 반환한다.
}
@RequestMapping(value = "email",
method = RequestMethod.POST,
produces = MediaType.APPLICATION_JSON_VALUE)
// 응답을 JSON으로 해석해야 한다는 조언이다.
// 맵핑, 주소값은 localhost:8080/member/email임, POST방식으로 요청한다. 응답을 MediaType의 APPLICATION_JSON_VALUE로 돌려주겠다.
@ResponseBody
public String postEmail(UserEntity user, EmailAuthEntity emailAuth) throws NoSuchAlgorithmException, MessagingException {
Enum<? extends IResult> result = this.memberService.sendEmailAuth(user, emailAuth);
// ?만 적어줘도 됨.
// 브라우저에게 salt 값을 넘겨줘야 되는데 user,까지 적어주면 솔트값을 못전해줌 emailAuth에서 솔트 지정해주면 그 값이 넘어오기 때문에 emailAuth를 꼭 써주어야 한다.
JSONObject responseObject = new JSONObject();
responseObject.put("result", result.name().toLowerCase());
// result의 name메서드를 불러와 소문자화 하겠다.
if (result == CommonResult.SUCCESS) {
responseObject.put("salt", emailAuth.getSalt());
}
//F12에 보면 솔트값도 넘어옴. DB에도 찍힌다.
return responseObject.toString();
// {
// "result":"success"
// } 를 브라우저가 반환한다. 솔트값을 추가로 알려줌. result가 success일때만 반환.
// 소문자화 하는 이유는 자바로 개발했다는 걸 숨기기 위함임.
}
@RequestMapping(value = "register",
method = RequestMethod.POST,
produces = MediaType.APPLICATION_JSON_VALUE)
@ResponseBody
public String postRegister(UserEntity user, EmailAuthEntity emailAuth) throws NoSuchAlgorithmException {
Enum<?> result = this.memberService.register(user, emailAuth);
// 1. 'MemberService' 가 가진 'register' 메서드에 'user' 및 'emailAuth' 전달하여 호출하기.
JSONObject responseObject = new JSONObject();
// 2. <1>이 반환하는 결과 'Enum<?>'를 받아와 'JSONObject' 타입의 응답 결과 만들기.
responseObject.put("result", result.name().toLowerCase());
//"result" 는 자바스크립트에도 switch문에 바꿔야 하고
if (result == CommonResult.SUCCESS) {
responseObject.put("salt", emailAuth.getSalt());
}
return responseObject.toString();
// 3. <2>에서 만들어진 'JSONObject' 객체를 문자열화(toString) 하여 반환하기.
}
@RequestMapping(value = "email",
method = RequestMethod.PATCH,
produces = MediaType.APPLICATION_JSON_VALUE)
@ResponseBody
public String patchEmail(EmailAuthEntity emailAuth) {
Enum<?> result = this.memberService.verifyEmailAuth(emailAuth);
JSONObject responseObject = new JSONObject();
responseObject.put("result", result.name().toLowerCase());
return responseObject.toString();
}
// 비밀번호 재설정하기
@RequestMapping(value = "recoverPassword",
method = RequestMethod.GET,
produces = MediaType.TEXT_HTML_VALUE)
public ModelAndView getRecoverPassword() {
ModelAndView modelAndView = new ModelAndView("member/recoverPassword");
return modelAndView;
}
@RequestMapping(value = "recoverPassword",
method = RequestMethod.POST,
produces = MediaType.APPLICATION_JSON_VALUE)
@ResponseBody
public String postRecoverPassword(EmailAuthEntity emailAuth) throws MessagingException {
Enum<?> result = this.memberService.recoverPasswordSend(emailAuth); // this.이 자리에 SUCCESS 또는 FAILURE 로 바뀜쓰
JSONObject responseObject = new JSONObject();
responseObject.put("result", result.name().toLowerCase());
if (result == CommonResult.SUCCESS) {
responseObject.put("index", emailAuth.getIndex());
}
return responseObject.toString(); // "{"result":"success"}"
}
@RequestMapping(value = "recoverPasswordEmail",
method = RequestMethod.POST,
produces = MediaType.APPLICATION_JSON_VALUE)
@ResponseBody
public String postRecoverPasswordEmail(EmailAuthEntity emailAuth) {
Enum<?> result = this.memberService.recoverPasswordcheck(emailAuth);
JSONObject responseObject = new JSONObject();
responseObject.put("result", result.name().toLowerCase());
if (result == CommonResult.SUCCESS) {
responseObject.put("code", emailAuth.getCode());
responseObject.put("salt", emailAuth.getSalt());
}
return responseObject.toString();
}
@RequestMapping(value = "recoverPasswordEmail",
method = RequestMethod.GET,
produces = MediaType.TEXT_HTML_VALUE)
public ModelAndView getRecoverPasswordEmail(EmailAuthEntity emailAuth) {
Enum<?> result = this.memberService.recoverPasswordAuth(emailAuth);
ModelAndView modelAndView = new ModelAndView("member/recoverPasswordEmail");
modelAndView.addObject("result", result.name());
return modelAndView;
}
@ResponseBody
public JSONObject recoverPasswordAuth(EmailAuthEntity emailAuth) {
Enum<? extends IResult> result = this.memberService.recoverPasswordAuth(emailAuth);
JSONObject responseObject = new JSONObject();
responseObject.put("result", result.name().toLowerCase());
if (result == CommonResult.SUCCESS) {
responseObject.put("code", emailAuth.getCode());
responseObject.put("salt", emailAuth.getSalt());
}
return null;
}
@RequestMapping(value = "recoverPassword",
method = RequestMethod.PATCH,
produces = MediaType.APPLICATION_JSON_VALUE)
@ResponseBody
public String patchRecoverPassword(EmailAuthEntity emailAuth, UserEntity user) {
Enum<?> result = this.memberService.recoverPassword(emailAuth, user);
JSONObject responseObject = new JSONObject();
responseObject.put("result", result.name().toLowerCase());
return responseObject.toString();
}
// 로그인
@RequestMapping(value = "login",
method = RequestMethod.GET,
produces = MediaType.TEXT_HTML_VALUE)
public ModelAndView getLogin() {
ModelAndView modelAndView = new ModelAndView("member/login");
return modelAndView;
}
@RequestMapping(value = "login",
method = RequestMethod.POST,
produces = MediaType.APPLICATION_JSON_VALUE)
@ResponseBody
public String postLogin(UserEntity user) {
Enum<?> result = this.memberService.login(user);
JSONObject responseObject = new JSONObject();
responseObject.put("result", result.name().toLowerCase());
return responseObject.toString();
}
// 이메일 찾기
@RequestMapping(value = "recoverEmail",
method = RequestMethod.GET,
produces = MediaType.TEXT_HTML_VALUE)
public ModelAndView getRecoverEmail() {
ModelAndView modelAndView = new ModelAndView("member/recoverEmail");
return modelAndView;
}
@RequestMapping(value = "recoverEmail",
method = RequestMethod.POST,
produces = MediaType.APPLICATION_JSON_VALUE)
@ResponseBody
public String postRecoverEmail(UserEntity user) {
Enum<?> result = this.memberService.recoverEmail(user);
JSONObject responseObject = new JSONObject();
responseObject.put("result", result.name().toLowerCase());
return responseObject.toString();
}
}
MemberService.java
package dev.xowoony.studymemberbbs.services;
import dev.xowoony.studymemberbbs.entities.member.EmailAuthEntity;
import dev.xowoony.studymemberbbs.entities.member.UserEntity;
import dev.xowoony.studymemberbbs.enums.CommonResult;
import dev.xowoony.studymemberbbs.enums.member.RegisterResult;
import dev.xowoony.studymemberbbs.enums.member.SendEmailAuthResult;
import dev.xowoony.studymemberbbs.enums.member.VerifyEmailAuthResult;
import dev.xowoony.studymemberbbs.interfaces.IResult;
import dev.xowoony.studymemberbbs.mappers.IMemberMapper;
import dev.xowoony.studymemberbbs.utils.CryptoUtils;
import org.apache.commons.lang3.RandomStringUtils;
import org.apache.commons.lang3.time.DateUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.mail.javamail.JavaMailSender;
import org.springframework.mail.javamail.MimeMessageHelper;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.thymeleaf.TemplateEngine;
import org.thymeleaf.context.Context;
import javax.mail.MessagingException;
import javax.mail.internet.MimeMessage;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Date;
//서비스 로직 구현
// 1. 받은 이메일을 사용하는 유저 레코드가 있는가? 있으면 실패로 결과를 돌려줘야 함. => "이미 사용중인 이메일입니다."
// 2. (Apache Commons Langs 사용하여) 인증번호 및 솔트(Salt)생성 후 테이블에 인서트
// 3. 2에서 생성된 인증번호를 전송 -> "인증 번호를 전송하였습니다. 5분 이내에 입력해 주세요."
// 3개중 하나라도 제대로 안되면 롤백 (트랜젝션을 적용해주어야 함)
//이메일인증이라는 목적 하에 3개가 1개 단위가 된다.
// 서버가 껐다 켜져도 기억하고 있으려면 DB에 저장을 시켜줘야 한다.
// 인증번호를 발송
// 서버 - > 인증번호 -> 이메일로 전송
// 서버는 솔트키를 이용해 클라이언트에게 넘겨줌 랜덤하게 만들어준 키. 어딘가에 숨겨져있다.
// 요청이 들어오면 이메일로 6자리 보내주고 128자 솔트키가 자바스크립트를 통해 회원가입 페이지로 보내준다
// 눌렀을때 솔트랑 인증번호 두개가 넘어옴. 서버로.
// 브라우저쪽으로는 솔트가
// 이메일로는 6자리가 넘어옴
// 솔트, 인증번호 세트가 서버로 넘어옴
//솔트가 빠지면 인증번호로만 인증을 시켜줘야되는 일이 발생
//MemberService가 IMemberMapper에 의존적이도록 만들기
@Service(value = "dev.xowoony.studymemberbbs.services.MemberService")
// @Service -> 스프링부트가 인식해야하는 서비스임을 알리는 어노테이션
public class MemberService {
// MemberService랑 IMemberMapper를 서로 의존적이게 만든다.
private final JavaMailSender mailSender; //의존성 주입
private final TemplateEngine templateEngine; // 템플릿 엔진
private final IMemberMapper memberMapper;
// alt+insert 생성자로 가던가 alt+enter해서 제일 위에거 클릭 해주면 쭈르륵 나와요
//IMemberMapper인터페이스 뒤에오는 membermapper는 내가 정하면 된다.
@Autowired
//의존성 주입을 위해 사용하는 어노테이션인 Autowired
public MemberService(JavaMailSender mailSender, TemplateEngine templateEngine, IMemberMapper MemberMapper) {
this.mailSender = mailSender;
this.templateEngine = templateEngine;
this.memberMapper = MemberMapper;
// this. 자기생성자 호출. 어떤 참조타입이 객체화 되기 위해 반드시 한번 실행되어야 한다.
}
// 3개를 여기 메서드에 모두 적겠다.
// 트랜잭션 적용하기 위해서
@Transactional
// Transactional은 3개중 하나라도 제대로 안되면 전부다 취소시키는 것을 구현
// 누구한테 보낼지?
// SendEmailAuthResult, CommonResult 두개를 받아와야함.
// Enum<?> 는 타입 즉 제네릭을 구분하지 않으며, 아무 열거형이나 다 반환이 가능하다..
// Enum을 적어주는 이유는 두개의 공통된 것을 적어줘야 받아올 수 있기 때문
// 매개변수로 유저엔티티를 받는다 user에게 이메일인증을 전송하겠다.
// 제한적으로 사용되므로 열거형을 써야한다.
// ABCD : Enum<ABCD>
// 열거형이라면 Enum
public Enum<? extends IResult> sendEmailAuth(UserEntity user, EmailAuthEntity emailAuth)
throws NoSuchAlgorithmException, MessagingException {
// 뭔지 모르겠는데 IResult 인터페이스를 상속받거나 구현하고 있어야 한다라는 표현이다.
// enum 인데 제네릭을 구분하지는 않겠다.
// 매개변수로 UserEntity를 받는다
UserEntity existingUser = this.memberMapper.selectUserByEmail(user.getEmail());
// user가 가지고 있는 Email값을 넣는다. 여기 user는 위 user랑 같다. 위 user는 컨트롤러가 준다.
if (existingUser != null) {
// 이미 사용중인 이메일이라면 (값이 있다면)
return SendEmailAuthResult.EMAIL_DUPLICATED;
// 이자리에서 메서드 실행이 끝난다.
// 이메일이 복사되었다.
}
String authCode = RandomStringUtils.randomNumeric(6);
// 테스트코드로 해볼거에요. (test디렉토리-> RandomTest에서)
// 랜덤한 숫자 문자열 6자리를 돌려줌
// apache.commons 의존성 추가 후 작성
String authSalt = String.format("%s%s%f%f",
user.getEmail(),
authCode,
Math.random(),
Math.random());
StringBuilder authSaltHashBuilder = new StringBuilder();
MessageDigest md = MessageDigest.getInstance("SHA-512");
md.update(authSalt.getBytes(StandardCharsets.UTF_8));
for (byte hashByte : md.digest()) {
authSaltHashBuilder.append(String.format("%02x", hashByte));
}
authSalt = authSaltHashBuilder.toString();
Date createdOn = new Date();
Date expiresOn = DateUtils.addMinutes(createdOn, 5);
emailAuth.setEmail(user.getEmail());
emailAuth.setCode(authCode);
emailAuth.setSalt(authSalt);
emailAuth.setCreatedOn(createdOn);
emailAuth.setExpiresOn(expiresOn);
emailAuth.setExpired(false);
if (this.memberMapper.insertEmailAuth(emailAuth) == 0) {
return CommonResult.FAILURE;
}
// 여기부턴 간단한 텍스트 파일 보낼 때 (웬만하면 쓸일이 없겠음)
// SimpleMailMessage mail = new SimpleMailMessage();
// mail.setFrom("xowoony@gmail.com");
// mail.setTo(user.getEmail());
// mail.setSubject("[스터디] 회원가입 인증 번호"); // 제목
// mail.setText(emailAuth.getCode()); // 인증번호
// this.mailSender.send(mail);
Context context = new Context();
// 타임리프꺼를 써야함
// 서비스에서 html로 인증코드를 넘겨줘야 하는데 그게 Context가 하는 일이다.
context.setVariable("code", emailAuth.getCode());
String text = this.templateEngine.process("member/registeremailAuth", context);
// modelandview뭐시기 거기랑 똑같소
MimeMessage mail = this.mailSender.createMimeMessage();
MimeMessageHelper helper = new MimeMessageHelper(mail, "UTF-8");
helper.setFrom("xowoony@gmail.com");
helper.setTo(user.getEmail());
helper.setSubject("[스터디] 회원가입 인증 번호");
helper.setText(text, true);
this.mailSender.send(mail);
return CommonResult.SUCCESS;
}
@Transactional
public Enum<? extends IResult> verifyEmailAuth(EmailAuthEntity emailAuth) {
// emailAuth는 컨트롤러가 준것이다.
EmailAuthEntity existingEmailAuth = this.memberMapper.selectEmailAuthByEmailCodeSalt(
// existingEmailAuth 는 모든 값을 다 가지고 있다.
emailAuth.getEmail(),
emailAuth.getCode(),
emailAuth.getSalt());
if (existingEmailAuth == null) {
// 3개 중 한 개 이상이 잘못되었을 경우 즉 인증번호가 잘못되었을 경우
return CommonResult.FAILURE;
}
// 5분 지났을 경우(만료)
if (existingEmailAuth.getExpiresOn().compareTo(new Date()) < 0) {
// getExpiresOn(만료시점) 에서 new Date 를 빼줬다고 생각하라.
return VerifyEmailAuthResult.EXPIRED;
}
//expired 올바른 인증번호를 입력하고 누르면 True가 되어야만 한다.
existingEmailAuth.setExpired(true);
if (this.memberMapper.updateEmailAuth(existingEmailAuth) == 0) {
// 영향을 받은 레코드 갯수가 0일 경우
return CommonResult.FAILURE;
}
return CommonResult.SUCCESS;
}
public Enum<? extends IResult> register(UserEntity user, EmailAuthEntity emailAuth) {
// 1. 'emailAuth'가 가진 'email', 'code', 'salt'값 기준으로 새로운 'EmailAuthEntity' SELECT 해서 가져오기
EmailAuthEntity existingEmailAuth = this.memberMapper.selectEmailAuthByEmailCodeSalt(
// 값 세개를 넘겨받는다.
emailAuth.getEmail(),
emailAuth.getCode(),
emailAuth.getSalt());
// 2. <1>에서 가져온 새로운 객체가 null 이거나 이가 가진 isExpired() 호출 결과가 false 인 경우 'RegisterResult.EMAIL_NOT_VERIFIED'를 결과로 반환하기. (없음으로 만들어야 함).
if (existingEmailAuth == null || !existingEmailAuth.isExpired()) {
// 이메일인증을 정상적으로 완료하지 않았을 때
// RegisterResult enums만들어 주고 와야함.
return RegisterResult.EMAIL_NOT_VERIFIED;
// 거절.
}
user.setPassword(CryptoUtils.hashSha512(user.getPassword()));
// 비밀번호 암호화 (해싱)
// CryptoUtils에 따로 빼놓은 것을 불러온다. 비밀번호가 해싱된 채로 반환 된 것을.
//3. 'user'객체를 'IMemberMapper' 객체의 'insertUser' 메서드 호출시 전달인자로 하여 INSERT 하기.
// 4. <3> 의 결과가 0이면, 'CommonResult.FAILURE' 반환하기.
if (this.memberMapper.insertUser(user) == 0) {
// 뭔지 모르겠지만 인서트가 안되었을 경우
return CommonResult.FAILURE;
// 실패
}
// 5. 위 과정 전체를 거친 후 'CommonResult.SUCCESS' 반환하기.
return CommonResult.SUCCESS;
}
@Transactional
// recoverPasswordSend 리펙터링 하라는데
public Enum<? extends IResult> recoverPasswordSend(EmailAuthEntity emailAuth) throws MessagingException {
UserEntity existingUser = this.memberMapper.selectUserByEmail(emailAuth.getEmail());
// user가 가지고 있는 Email값을 넣는다. 여기 user는 위 user랑 같다. 위 user는 컨트롤러가 준다.
if (existingUser == null) {
// 이미 사용중인 이메일이라면 (값이 없다면)
return CommonResult.FAILURE;
// 이자리에서 메서드 실행이 끝난다.
// 이메일이 복사되었다.
// SUCCESS의 경우
}
String authCode = RandomStringUtils.randomNumeric(6);
String authSalt = String.format("%s%s%f%f",
authCode,
emailAuth.getEmail(),
Math.random(),
Math.random());
authSalt = CryptoUtils.hashSha512(authSalt);
Date createOn = new Date(); // 현재일시
Date expiresOn = DateUtils.addMinutes(createOn, 5); // 5분 미래
emailAuth.setCode(authCode);
emailAuth.setSalt(authSalt);
emailAuth.setCreatedOn(createOn);
emailAuth.setExpiresOn(expiresOn);
emailAuth.setExpired(false);
if (this.memberMapper.insertEmailAuth(emailAuth) == 0) {
return CommonResult.FAILURE;
}
Context context = new Context();
context.setVariable("email", emailAuth.getEmail());
context.setVariable("code", emailAuth.getCode());
context.setVariable("salt", emailAuth.getSalt());
// 타임리프꺼를 써야함
// 서비스에서 html로 인증코드를 넘겨줘야 하는데 그게 Context가 하는 일이다.
context.setVariable("code", emailAuth.getCode());
String text = this.templateEngine.process("member/recoverPasswordemailAuth", context);
// modelandview뭐시기 거기랑 똑같소
MimeMessage mail = this.mailSender.createMimeMessage();
MimeMessageHelper helper = new MimeMessageHelper(mail, "UTF-8");
helper.setFrom("xowoony@gmail.com");
helper.setTo(emailAuth.getEmail());
helper.setSubject("[스터디] 비밀번호 재설정 인증 링크");
helper.setText(text, true);
this.mailSender.send(mail);
return CommonResult.SUCCESS;
// 사용중인 이메일이 면 success.
}
public Enum<? extends IResult> recoverPasswordcheck(EmailAuthEntity emailAuth) {
// 적기 전에 Membermapper.xml select 하고
EmailAuthEntity existingEmailAuth = this.memberMapper.selectEmailAuthByIndex(emailAuth.getIndex());
if (existingEmailAuth == null || !existingEmailAuth.isExpired()) {
return CommonResult.FAILURE;
}
emailAuth.setCode(existingEmailAuth.getCode());
emailAuth.setSalt(existingEmailAuth.getSalt());
return CommonResult.SUCCESS;
}
@Transactional
public Enum<? extends IResult> recoverPasswordAuth(EmailAuthEntity emailAuth) {
EmailAuthEntity existingEmailAuth = this.memberMapper.selectEmailAuthByEmailCodeSalt(
emailAuth.getEmail(),
emailAuth.getCode(),
emailAuth.getSalt());
if (existingEmailAuth == null) {
return CommonResult.FAILURE;
}
if (new Date().compareTo(existingEmailAuth.getExpiresOn()) > 0) {
return CommonResult.FAILURE;
}
existingEmailAuth.setExpired(true);
if (this.memberMapper.updateEmailAuth(existingEmailAuth) == 0) {
return CommonResult.FAILURE;
}
return CommonResult.SUCCESS;
}
// 비밀번호 재설정 // 1109 과제
@Transactional
public Enum<? extends IResult> recoverPassword(EmailAuthEntity emailAuth, UserEntity user) {
//
EmailAuthEntity existingEmailAuth = this.memberMapper.selectEmailAuthByEmailCodeSalt(
emailAuth.getEmail(),
emailAuth.getCode(),
emailAuth.getSalt());
if (existingEmailAuth == null || !existingEmailAuth.isExpired()) {
return CommonResult.FAILURE;
}
// 이메일 인증이 아직 안됐거나 인증 시간이 5분이 지나서 만료되었을 경우 실패를 반환한다.
UserEntity existingUser = this.memberMapper.selectUserByEmail(existingEmailAuth.getEmail());
// 이메일값을 기준으로 DB에서 셀렉트 해온 값.existingUser는 DB에서 왔다.
existingUser.setPassword(CryptoUtils.hashSha512(user.getPassword()));
// 새롭게 입력한 비밀번호로 해싱 후 비밀번호를 수정한다.
if (this.memberMapper.updateUser(existingUser) == 0) {
// 데이터를 싹다 가지고 와서 그중에 비밀번호만 바꾸고 다시 집어넣는 과정이다.
return CommonResult.FAILURE;
}
return CommonResult.SUCCESS;
}
// 로그인
@Transactional
public Enum<? extends IResult> login(UserEntity user) {
UserEntity existingUser = this.memberMapper.selectUserByEmailPassword(
user.getEmail(),
CryptoUtils.hashSha512(user.getPassword()));
if (existingUser == null) {
System.out.println("실패!!!!");
return CommonResult.FAILURE;
}
System.out.println("성공!!!!");
return CommonResult.SUCCESS;
}
// 이메일 찾기
@Transactional
public Enum<? extends IResult> recoverEmail(UserEntity user) {
UserEntity findEmail = this.memberMapper.selectUserByNameContact(
user.getName(),
user.getContact());
if (findEmail == null) {
return CommonResult.FAILURE;
}
return CommonResult.SUCCESS;
}
}
MemberMapper.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="dev.xowoony.studymemberbbs.mappers.IMemberMapper">
<!--인서트-->
<insert id="insertEmailAuth"
useGeneratedKeys="true"
keyColumn="index"
keyProperty="index"
parameterType="dev.xowoony.studymemberbbs.entities.member.EmailAuthEntity">
INSERT INTO `study_member`.`email_auths` (`email`, `code`, `salt`, `created_on`, `expires_on`, `expired_flag`)
VALUES (#{email}, #{code}, #{salt}, #{createdOn}, #{expiresOn}, #{isExpired})
</insert>
<insert id="insertUser"
keyColumn="email"
keyProperty="email"
parameterType="dev.xowoony.studymemberbbs.entities.member.UserEntity">
INSERT INTO `study_member`.`users` (`email`, `password`, `nickname`, `name`, `contact`, `address_postal`,
`address_primary`, `address_secondary`)
VALUES (#{email}, #{password}, #{nickname}, #{name}, #{contact}, #{addressPostal}, #{addressPrimary},
#{addressSecondary})
</insert>
<select id="selectEmailAuthByEmailCodeSalt"
resultType="dev.xowoony.studymemberbbs.entities.member.EmailAuthEntity">
SELECT `index` AS `index`,
`email` AS `email`,
`code` AS `code`,
`salt` AS `salt`,
`created_on` AS `createdOn`,
`expires_on` AS `expiresOn`,
`expired_flag` AS `isExpired`
FROM `study_member`.`email_auths`
WHERE BINARY `email` = #{email}
AND BINARY `code` = #{code}
AND BINARY `salt` = #{salt}
/*BINARY는 웬만하면 하세요 안해도 되지만*/
LIMIT 1
</select>
<select id="selectEmailAuthByIndex"
resultType="dev.xowoony.studymemberbbs.entities.member.EmailAuthEntity">
SELECT `index` AS `index`,
`email` AS `email`,
`code` AS `code`,
`salt` AS `salt`,
`created_on` AS `createdOn`,
`expires_on` AS `expiresOn`,
`expired_flag` AS `isExpired`
FROM `study_member`.`email_auths`
WHERE BINARY `index` = #{index}
</select>
<select id="selectUserByEmailPassword"
resultType="dev.xowoony.studymemberbbs.entities.member.UserEntity">
SELECT `email` AS `email`,
`password` AS `password`,
`nickname` AS `nickname`,
`name` AS `name`,
`contact` AS `contact`,
`address_postal` AS `addressPostal`,
`address_primary` AS `addressPrimary`,
`address_secondary` AS `addressSecondary`,
`registered_on` AS `registeredOn`
FROM `study_member`.`users`
WHERE BINARY `email` = #{email}
AND BINARY `password` = #{password}
LIMIT 1
</select>
<select id="selectUserByEmail"
resultType="dev.xowoony.studymemberbbs.entities.member.UserEntity">
/*id를 selectUserByEmail 로 준다.*/
/*select는 resultType이 있어야 한다.*/
/*별명은 UserEntity에 setEmail 이런식으로 있어야 하오*/
SELECT `email` AS `email`,
`password` AS `password`,
`nickname` AS `nickname`,
`name` AS `name`,
`contact` AS `contact`,
`address_postal` AS `addressPostal`,
`address_primary` AS `addressPrimary`,
`address_secondary` AS `addressSecondary`,
`registered_on` AS `registeredOn`
FROM `study_member`.`users`
WHERE BINARY `email` = #{email}
LIMIT 1
</select>
<update id="updateEmailAuth"
parameterType="dev.xowoony.studymemberbbs.entities.member.EmailAuthEntity">
UPDATE `study_member`.`email_auths`
SET `email` = #{email},
`code` = #{code},
`salt` = #{salt},
`created_on` = #{createdOn},
`expires_on` = #{expiresOn},
`expired_flag` = #{isExpired}
WHERE BINARY `index` = #{index}
LIMIT 1
</update>
<update id="updateUser"
parameterType="dev.xowoony.studymemberbbs.entities.member.UserEntity">
UPDATE `study_member`.`users`
SET `password` = #{password},
`nickname` = #{nickname},
`name` = #{name},
`contact`= #{contact},
`address_postal` = #{addressPostal},
`address_secondary` = #{addressSecondary},
`registered_on` = #{registeredOn}
WHERE BINARY `email` = #{email}
LIMIT 1
</update>
<select id="selectUserByNameContact"
resultType="dev.xowoony.studymemberbbs.entities.member.UserEntity">
SELECT `email` AS `email`,
`password` AS `password`,
`nickname` AS `nickname`,
`name` AS `name`,
`contact` AS `contact`,
`address_postal` AS `addressPostal`,
`address_primary` AS `addressPrimary`,
`address_secondary` AS `addressSecondary`,
`registered_on` AS `registeredOn`
FROM `study_member`.`users`
WHERE BINARY `name` = #{name}
AND BINARY `contact` = #{contact}
LIMIT 1
</select>
</mapper>
IMemberMapper.java (인터페이스)
package dev.xowoony.studymemberbbs.mappers;
import dev.xowoony.studymemberbbs.entities.member.EmailAuthEntity;
import dev.xowoony.studymemberbbs.entities.member.UserEntity;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
// mybatis-spring-boot-starter 의존성을 주입시켰기 때문에 인식이 가능하다.
@Mapper
// 맵핑
public interface IMemberMapper {
int insertEmailAuth(EmailAuthEntity emailAuthEntity);
// param 어노테이션, @Param은 여러개를 불러올 수 있다.
EmailAuthEntity selectEmailAuthByEmailCodeSalt(@Param(value = "email") String email,
@Param(value = "code") String code,
@Param(value = "salt") String salt);
EmailAuthEntity selectEmailAuthByIndex(@Param(value = "index") int index);
UserEntity selectUserByEmail(@Param(value = "email") String email);
UserEntity selectUserByEmailPassword(@Param(value = "email") String email,
@Param(value = "password") String password);
int updateEmailAuth(EmailAuthEntity emailAuth);
// int타입을 반환하는 updateEmailAuth => 영향을 받은 갯수.
int insertUser(UserEntity user);
int updateUser(UserEntity user);
// updateUser 라고 만들어놓으면 password 등등 많이 쓰일일이 있으므로 편함
// 이메일 인증 구현
UserEntity selectUserByNameContact(@Param(value = "name") String name,
@Param(value = "contact") String contact);
}
'Spring Boot > 구현해보기' 카테고리의 다른 글
게시판 글쓰기 구현 + DB에 인서트 하기 (0) | 2022.11.17 |
---|---|
게시판 글쓰기 구현 (0) | 2022.11.14 |
비밀번호 재설정 구현2 (0) | 2022.11.13 |
비밀번호 재설정 구현 (0) | 2022.11.13 |
로그인 페이지 구현 (0) | 2022.11.12 |
Comments