Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Feat-44] : 인증 메일 전송 및 검증 기능 구현 #45

Open
wants to merge 6 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -28,10 +28,15 @@ dependencies {
implementation 'org.springframework.boot:spring-boot-starter-validation'
implementation 'org.projectlombok:lombok'

//email 인증
implementation 'org.springframework.boot:spring-boot-starter-mail'

// database
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
implementation 'org.springframework.boot:spring-boot-starter-data-mongodb'
implementation 'org.springframework.boot:spring-boot-starter-data-redis'

runtimeOnly 'com.mysql:mysql-connector-j'
runtimeOnly 'com.h2database:h2'
compileOnly 'org.projectlombok:lombok'
annotationProcessor 'org.projectlombok:lombok'
Expand Down
4 changes: 4 additions & 0 deletions src/main/java/icurriculum/IcurriculumApplication.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,12 @@

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.data.jpa.repository.config.EnableJpaAuditing;
import org.springframework.data.redis.repository.configuration.EnableRedisRepositories;

@SpringBootApplication
@EnableRedisRepositories
@EnableJpaAuditing
public class IcurriculumApplication {

public static void main(String[] args) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,11 @@ public enum ErrorStatus {
UNAUTHORIZED(HttpStatus.UNAUTHORIZED, "COMMON401", "로그인 인증이 필요합니다."),
FORBIDDEN(HttpStatus.FORBIDDEN, "COMMON403", "금지된 요청입니다."),

/*
* mail
*/
MAIL_NOT_FOUND(HttpStatus.BAD_REQUEST, "MAIL400","잘못된 메일입니다."),
MAIL_NOT_SEND(HttpStatus.BAD_REQUEST, "MAIL401", "메일 전송에 실패했습니다."),
/*
* take
*/
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package icurriculum.global.verification.controller;


import icurriculum.global.response.ApiResponse;
import icurriculum.global.response.status.ErrorStatus;
import icurriculum.global.verification.dto.request.MailRequestDTO;
import icurriculum.global.verification.dto.response.MailResponseDTO;
import icurriculum.global.verification.service.EmailService;
import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import static icurriculum.global.verification.dto.response.MailResponseDTO.*;

@RestController
@RequestMapping("/mail")
@RequiredArgsConstructor
public class MailController {

private final EmailService emailService;

@PostMapping("/send")
public ApiResponse<MailSend> mailSend(@RequestBody MailRequestDTO.MailSend request) {
MailSend mailSend = emailService.integratedProcee(request.getEmail());
if (!mailSend.getStatus()) {
return ApiResponse.onFailure(ErrorStatus.MAIL_NOT_SEND, mailSend);
}
return ApiResponse.onSuccess(mailSend);
}

@PostMapping("/verify")
public ApiResponse<MailVerify> mailVerify(@RequestBody @Valid MailRequestDTO.MailVerify request) {
MailVerify mailVerify = emailService.verifyCode(request.getEmail(), request.getCode());
return ApiResponse.onSuccess(mailVerify);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package icurriculum.global.verification.converter;

import icurriculum.global.verification.dto.response.MailResponseDTO;
import org.springframework.stereotype.Component;

import java.time.LocalDateTime;

import static icurriculum.global.verification.dto.response.MailResponseDTO.*;

@Component
public class EmailConverter {

public MailSend toMailSendResponse(String comment, Boolean status) {
return MailSend.builder()
.responseComment(comment)
.status(status)
.timeStamp(LocalDateTime.now())
.build();
}

public MailVerify toMailVerifyResponse(Boolean check, String email) {
return MailVerify.builder()
.check(check)
.email(email)
.build();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package icurriculum.global.verification.dto.request;

import jakarta.validation.constraints.Email;
import jakarta.validation.constraints.NotBlank;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;

public abstract class MailRequestDTO {

@Builder
@Getter
@AllArgsConstructor
@NoArgsConstructor
public static class MailSend {
@Email
@NotBlank
private String email;
}

@Builder
@Getter
@AllArgsConstructor
public static class MailVerify {
@Email
@NotBlank
private String email;
@NotBlank
private String code;
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package icurriculum.global.verification.dto.response;

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;

import java.time.LocalDateTime;

public abstract class MailResponseDTO {

@Builder
@Getter
@AllArgsConstructor
public static class MailSend{
private String responseComment;
private Boolean status;
private LocalDateTime timeStamp;
}


@Builder
@Getter
@AllArgsConstructor
public static class MailVerify{
private Boolean check;
private String email;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package icurriculum.global.verification.entity;

import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import org.springframework.data.annotation.Id;
import org.springframework.data.redis.core.RedisHash;

import java.io.Serializable;


@RedisHash(value = "Email", timeToLive = 600) //Redis 저장 엔티티, 제한시간 10분
@AllArgsConstructor
@Getter
@Setter
public class EmailVerification implements Serializable {
@Id
private String email;
private String verificationCode;
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package icurriculum.global.verification.repository;

import icurriculum.global.verification.entity.EmailVerification;
import org.springframework.data.repository.CrudRepository;
import org.springframework.stereotype.Repository;

@Repository
public interface EmailVerificationRepository extends CrudRepository<EmailVerification, String> {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
package icurriculum.global.verification.service;

import icurriculum.global.verification.converter.EmailConverter;
import icurriculum.global.verification.dto.response.MailResponseDTO;
import icurriculum.global.verification.entity.EmailVerification;
import icurriculum.global.verification.repository.EmailVerificationRepository;
import jakarta.mail.MessagingException;
import jakarta.mail.internet.MimeMessage;
import lombok.RequiredArgsConstructor;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.mail.javamail.JavaMailSender;
import org.springframework.mail.javamail.MimeMessageHelper;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.Optional;
import java.util.Random;
import java.util.concurrent.TimeUnit;

import static icurriculum.global.verification.dto.response.MailResponseDTO.*;

@Service
@RequiredArgsConstructor
public class EmailService {

private final JavaMailSender mailSender;
private final EmailConverter emailConverter;
private final EmailVerificationRepository emailVerificationRepository;
private final RedisTemplate<String, Object> redisTemplate;

private final String SUBJECT = "[ICURRICULUM] 인하대학교 학생 인증 메일입니다.";

public String makeVerificationCode() {
Random random = new Random();
return String.format("%06d", random.nextInt(999999));
}
public String getRedisKey(String email) {
return "Email:" + email;
}

@Transactional
public MailSend sendVerificationEmail(String email, String code) {
try {
EmailVerification verification = new EmailVerification(email, code);

MimeMessage message = mailSender.createMimeMessage();
MimeMessageHelper messageHelper = new MimeMessageHelper(message, true);

messageHelper.setTo(email);
messageHelper.setSubject(SUBJECT);
String htmlContent = getVerificationMessage(code);
messageHelper.setText(htmlContent, true);

if (emailVerificationRepository.existsById(getRedisKey(email))) { //redis에 email이 있으면 삭제
emailVerificationRepository.deleteById(getRedisKey(email));
}
emailVerificationRepository.save(verification); //Redis에 인증 코드 저장
mailSender.send(message);

} catch (MessagingException e) {
e.printStackTrace();
return emailConverter.toMailSendResponse("메일 전송에 실패했습니다.", false);
}
return emailConverter.toMailSendResponse("메일 전송에 성공했습니다.", true);
}

@Transactional
public MailSend integratedProcee(String email) {
String code = makeVerificationCode();
return sendVerificationEmail(email, code);
}
Copy link
Author

@KSLEE19 KSLEE19 Dec 11, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

service단에서 모두 처리할 수 있도록 새로 추가한 '메일 전송 과정을 통합해서 실행'하는 메소드 입니다.


public String getVerificationMessage(String verificationCode) {
StringBuffer verificationMessage = new StringBuffer();
verificationMessage
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

나중에 근로생중 누군가 메일 폼 만들어 줄겁니다. 나중되면 그걸로 수정합시다

.append("<h1 style = 'text-align: center;'>[ICURRICULUM] 인증메일</h1>");
verificationMessage
.append("<h3 style ='text-align: center;'>인증코드 : <strong style='font-size: 32px; letter-spacing: 8px;'>")
.append(verificationCode)
.append("</strong></h3>");
return verificationMessage.toString();
}

public MailVerify verifyCode(String email, String code) {
Optional<EmailVerification> verificationObject = emailVerificationRepository.findById(email);
boolean check = false;
if (verificationObject.isPresent()) {
EmailVerification verification = verificationObject.get();
if (verification.getVerificationCode().equals(code)) {
emailVerificationRepository.deleteById(email);
check = true;
}
}
return emailConverter.toMailVerifyResponse(check, email);
}

public Long getTTL(String key) {
return redisTemplate.getExpire(key, TimeUnit.SECONDS);
}
}