From f98bbfdbaf5e606decf8e21ba448ab26a06f415b Mon Sep 17 00:00:00 2001 From: "LEE K.S" Date: Sun, 3 Nov 2024 19:45:35 +0900 Subject: [PATCH 1/6] =?UTF-8?q?feat:=20=EC=9D=B4=EB=A9=94=EC=9D=BC=20?= =?UTF-8?q?=EC=9D=B8=EC=A6=9D=20=EA=B8=B0=EB=8A=A5=20=ED=95=84=EC=9A=94=20?= =?UTF-8?q?=EC=9D=98=EC=A1=B4=EC=84=B1=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build.gradle | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/build.gradle b/build.gradle index 27e8fbe..82cbca7 100644 --- a/build.gradle +++ b/build.gradle @@ -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' From 7d898d003843cd2c1ca88be4c64286dccbd6beea Mon Sep 17 00:00:00 2001 From: "LEE K.S" Date: Sun, 3 Nov 2024 20:58:07 +0900 Subject: [PATCH 2/6] =?UTF-8?q?feat:=20=EC=9D=B4=EB=A9=94=EC=9D=BC=20?= =?UTF-8?q?=EA=B4=80=EB=A0=A8=20=EC=97=90=EB=9F=AC=EC=BD=94=EB=93=9C=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/icurriculum/global/response/status/ErrorStatus.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/main/java/icurriculum/global/response/status/ErrorStatus.java b/src/main/java/icurriculum/global/response/status/ErrorStatus.java index be40867..d4d25c8 100644 --- a/src/main/java/icurriculum/global/response/status/ErrorStatus.java +++ b/src/main/java/icurriculum/global/response/status/ErrorStatus.java @@ -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 */ From 5e4208345aa16ca67f4ab92acd3bb3b361503de3 Mon Sep 17 00:00:00 2001 From: "LEE K.S" Date: Sun, 3 Nov 2024 20:58:23 +0900 Subject: [PATCH 3/6] =?UTF-8?q?feat:=20=EC=9D=B8=EC=A6=9D=EB=A9=94?= =?UTF-8?q?=EC=9D=BC=20=EC=A0=84=EC=86=A1=20=EA=B8=B0=EB=8A=A5=20=EA=B5=AC?= =?UTF-8?q?=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/MailController.java | 34 +++++++++ .../converter/EmailConverter.java | 21 ++++++ .../dto/request/MailRequestDTO.java | 22 ++++++ .../dto/response/MailResponseDTO.java | 20 ++++++ .../entity/EmailVerification.java | 22 ++++++ .../EmailVerificationRepository.java | 9 +++ .../verification/service/EmailService.java | 71 +++++++++++++++++++ 7 files changed, 199 insertions(+) create mode 100644 src/main/java/icurriculum/global/verification/controller/MailController.java create mode 100644 src/main/java/icurriculum/global/verification/converter/EmailConverter.java create mode 100644 src/main/java/icurriculum/global/verification/dto/request/MailRequestDTO.java create mode 100644 src/main/java/icurriculum/global/verification/dto/response/MailResponseDTO.java create mode 100644 src/main/java/icurriculum/global/verification/entity/EmailVerification.java create mode 100644 src/main/java/icurriculum/global/verification/repository/EmailVerificationRepository.java create mode 100644 src/main/java/icurriculum/global/verification/service/EmailService.java diff --git a/src/main/java/icurriculum/global/verification/controller/MailController.java b/src/main/java/icurriculum/global/verification/controller/MailController.java new file mode 100644 index 0000000..2c416bc --- /dev/null +++ b/src/main/java/icurriculum/global/verification/controller/MailController.java @@ -0,0 +1,34 @@ +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 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(@RequestBody MailRequestDTO.MailSend request) { + String code = emailService.makeVerificationCode(); + String email = request.getEmail(); + MailSend mailSend = emailService.sendVerificationEmail(email, code); + if (!mailSend.getStatus()) { + return ApiResponse.onFailure(ErrorStatus.MAIL_NOT_SEND, mailSend); + } + return ApiResponse.onSuccess(mailSend); + } +} diff --git a/src/main/java/icurriculum/global/verification/converter/EmailConverter.java b/src/main/java/icurriculum/global/verification/converter/EmailConverter.java new file mode 100644 index 0000000..84c3f01 --- /dev/null +++ b/src/main/java/icurriculum/global/verification/converter/EmailConverter.java @@ -0,0 +1,21 @@ +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(); + } + +} diff --git a/src/main/java/icurriculum/global/verification/dto/request/MailRequestDTO.java b/src/main/java/icurriculum/global/verification/dto/request/MailRequestDTO.java new file mode 100644 index 0000000..186d974 --- /dev/null +++ b/src/main/java/icurriculum/global/verification/dto/request/MailRequestDTO.java @@ -0,0 +1,22 @@ +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; + } + +} diff --git a/src/main/java/icurriculum/global/verification/dto/response/MailResponseDTO.java b/src/main/java/icurriculum/global/verification/dto/response/MailResponseDTO.java new file mode 100644 index 0000000..c25cfb0 --- /dev/null +++ b/src/main/java/icurriculum/global/verification/dto/response/MailResponseDTO.java @@ -0,0 +1,20 @@ +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; + } + +} diff --git a/src/main/java/icurriculum/global/verification/entity/EmailVerification.java b/src/main/java/icurriculum/global/verification/entity/EmailVerification.java new file mode 100644 index 0000000..9c75573 --- /dev/null +++ b/src/main/java/icurriculum/global/verification/entity/EmailVerification.java @@ -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; +} + diff --git a/src/main/java/icurriculum/global/verification/repository/EmailVerificationRepository.java b/src/main/java/icurriculum/global/verification/repository/EmailVerificationRepository.java new file mode 100644 index 0000000..28cad7a --- /dev/null +++ b/src/main/java/icurriculum/global/verification/repository/EmailVerificationRepository.java @@ -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 { +} diff --git a/src/main/java/icurriculum/global/verification/service/EmailService.java b/src/main/java/icurriculum/global/verification/service/EmailService.java new file mode 100644 index 0000000..6e4e880 --- /dev/null +++ b/src/main/java/icurriculum/global/verification/service/EmailService.java @@ -0,0 +1,71 @@ +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.mail.javamail.JavaMailSender; +import org.springframework.mail.javamail.MimeMessageHelper; +import org.springframework.stereotype.Service; + +import java.util.Random; + +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 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; + } + + 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); + } + + public String getVerificationMessage(String verificationCode) { + StringBuffer verificationMessage = new StringBuffer(); + verificationMessage + .append("

[ICURRICULUM] 인증메일

"); + verificationMessage + .append("

인증코드 : ") + .append(verificationCode) + .append("

"); + return verificationMessage.toString(); + } +} From 893fca994500b121020c093a2229eca230affa80 Mon Sep 17 00:00:00 2001 From: "LEE K.S" Date: Sun, 3 Nov 2024 22:04:51 +0900 Subject: [PATCH 4/6] =?UTF-8?q?feat:=20Redis,=20Auditing=EC=9D=84=20?= =?UTF-8?q?=EC=9C=84=ED=95=9C=20=EC=96=B4=EB=85=B8=ED=85=8C=EC=9D=B4?= =?UTF-8?q?=EC=85=98=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/icurriculum/IcurriculumApplication.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/main/java/icurriculum/IcurriculumApplication.java b/src/main/java/icurriculum/IcurriculumApplication.java index 974c70c..af90a09 100644 --- a/src/main/java/icurriculum/IcurriculumApplication.java +++ b/src/main/java/icurriculum/IcurriculumApplication.java @@ -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) { From 44da1b397a1105c590fc717a8f5e0a7988ec453f Mon Sep 17 00:00:00 2001 From: "LEE K.S" Date: Sun, 3 Nov 2024 22:05:11 +0900 Subject: [PATCH 5/6] =?UTF-8?q?feat:=20=EC=9D=B8=EC=A6=9D=20=EB=B2=88?= =?UTF-8?q?=ED=98=B8=EB=A5=BC=20=ED=99=95=EC=9D=B8=ED=95=98=EB=8A=94=20?= =?UTF-8?q?=EA=B8=B0=EB=8A=A5=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/MailController.java | 7 +++++++ .../converter/EmailConverter.java | 6 ++++++ .../dto/request/MailRequestDTO.java | 11 ++++++++++ .../dto/response/MailResponseDTO.java | 8 +++++++ .../verification/service/EmailService.java | 21 +++++++++++++++++++ 5 files changed, 53 insertions(+) diff --git a/src/main/java/icurriculum/global/verification/controller/MailController.java b/src/main/java/icurriculum/global/verification/controller/MailController.java index 2c416bc..586fe67 100644 --- a/src/main/java/icurriculum/global/verification/controller/MailController.java +++ b/src/main/java/icurriculum/global/verification/controller/MailController.java @@ -6,6 +6,7 @@ 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; @@ -31,4 +32,10 @@ public ApiResponse mailSend(@RequestBody MailRequestDTO.MailSend reque } return ApiResponse.onSuccess(mailSend); } + + @PostMapping("/verify") + public ApiResponse mailVerify(@RequestBody @Valid MailRequestDTO.MailVerify request) { + MailVerify mailVerify = emailService.verifyCode(request.getEmail(), request.getCode()); + return ApiResponse.onSuccess(mailVerify); + } } diff --git a/src/main/java/icurriculum/global/verification/converter/EmailConverter.java b/src/main/java/icurriculum/global/verification/converter/EmailConverter.java index 84c3f01..4dc6213 100644 --- a/src/main/java/icurriculum/global/verification/converter/EmailConverter.java +++ b/src/main/java/icurriculum/global/verification/converter/EmailConverter.java @@ -18,4 +18,10 @@ public MailSend toMailSendResponse(String comment, Boolean status) { .build(); } + public MailVerify toMailVerifyResponse(Boolean check, String email) { + return MailVerify.builder() + .check(check) + .email(email) + .build(); + } } diff --git a/src/main/java/icurriculum/global/verification/dto/request/MailRequestDTO.java b/src/main/java/icurriculum/global/verification/dto/request/MailRequestDTO.java index 186d974..ccd8975 100644 --- a/src/main/java/icurriculum/global/verification/dto/request/MailRequestDTO.java +++ b/src/main/java/icurriculum/global/verification/dto/request/MailRequestDTO.java @@ -19,4 +19,15 @@ public static class MailSend { private String email; } + @Builder + @Getter + @AllArgsConstructor + public static class MailVerify { + @Email + @NotBlank + private String email; + @NotBlank + private String code; + } + } diff --git a/src/main/java/icurriculum/global/verification/dto/response/MailResponseDTO.java b/src/main/java/icurriculum/global/verification/dto/response/MailResponseDTO.java index c25cfb0..a5a5763 100644 --- a/src/main/java/icurriculum/global/verification/dto/response/MailResponseDTO.java +++ b/src/main/java/icurriculum/global/verification/dto/response/MailResponseDTO.java @@ -17,4 +17,12 @@ public static class MailSend{ private LocalDateTime timeStamp; } + + @Builder + @Getter + @AllArgsConstructor + public static class MailVerify{ + private Boolean check; + private String email; + } } diff --git a/src/main/java/icurriculum/global/verification/service/EmailService.java b/src/main/java/icurriculum/global/verification/service/EmailService.java index 6e4e880..bddb0c6 100644 --- a/src/main/java/icurriculum/global/verification/service/EmailService.java +++ b/src/main/java/icurriculum/global/verification/service/EmailService.java @@ -7,11 +7,14 @@ 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 java.util.Optional; import java.util.Random; +import java.util.concurrent.TimeUnit; import static icurriculum.global.verification.dto.response.MailResponseDTO.*; @@ -22,6 +25,7 @@ public class EmailService { private final JavaMailSender mailSender; private final EmailConverter emailConverter; private final EmailVerificationRepository emailVerificationRepository; + private final RedisTemplate redisTemplate; private final String SUBJECT = "[ICURRICULUM] 인하대학교 학생 인증 메일입니다."; @@ -68,4 +72,21 @@ public String getVerificationMessage(String verificationCode) { .append(""); return verificationMessage.toString(); } + + public MailVerify verifyCode(String email, String code) { + Optional 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); + } } From b86915d328399c3b835045af6bea62caa2c03b74 Mon Sep 17 00:00:00 2001 From: "LEE K.S" Date: Thu, 12 Dec 2024 03:46:57 +0900 Subject: [PATCH 6/6] =?UTF-8?q?fix:=20=EB=A9=94=EC=9D=BC=EC=9D=84=20?= =?UTF-8?q?=EC=A0=84=EC=86=A1=ED=95=98=EB=8A=94=20=EC=A3=BC=EC=9A=94=20?= =?UTF-8?q?=EB=A1=9C=EC=A7=81=EC=9D=84=20=EC=84=9C=EB=B9=84=EC=8A=A4?= =?UTF-8?q?=EB=8B=A8=EC=97=90=EC=84=9C=20=EB=AA=A8=EB=91=90=20=EC=B2=98?= =?UTF-8?q?=EB=A6=AC=ED=95=A0=20=EC=88=98=20=EC=9E=88=EB=8F=84=EB=A1=9D=20?= =?UTF-8?q?=EC=97=AD=ED=95=A0=20=EB=B6=84=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../global/verification/controller/MailController.java | 4 +--- .../global/verification/service/EmailService.java | 8 ++++++++ 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/src/main/java/icurriculum/global/verification/controller/MailController.java b/src/main/java/icurriculum/global/verification/controller/MailController.java index 586fe67..c3f83cb 100644 --- a/src/main/java/icurriculum/global/verification/controller/MailController.java +++ b/src/main/java/icurriculum/global/verification/controller/MailController.java @@ -24,9 +24,7 @@ public class MailController { @PostMapping("/send") public ApiResponse mailSend(@RequestBody MailRequestDTO.MailSend request) { - String code = emailService.makeVerificationCode(); - String email = request.getEmail(); - MailSend mailSend = emailService.sendVerificationEmail(email, code); + MailSend mailSend = emailService.integratedProcee(request.getEmail()); if (!mailSend.getStatus()) { return ApiResponse.onFailure(ErrorStatus.MAIL_NOT_SEND, mailSend); } diff --git a/src/main/java/icurriculum/global/verification/service/EmailService.java b/src/main/java/icurriculum/global/verification/service/EmailService.java index bddb0c6..6e39ead 100644 --- a/src/main/java/icurriculum/global/verification/service/EmailService.java +++ b/src/main/java/icurriculum/global/verification/service/EmailService.java @@ -11,6 +11,7 @@ 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; @@ -37,6 +38,7 @@ public String getRedisKey(String email) { return "Email:" + email; } + @Transactional public MailSend sendVerificationEmail(String email, String code) { try { EmailVerification verification = new EmailVerification(email, code); @@ -62,6 +64,12 @@ public MailSend sendVerificationEmail(String email, String code) { return emailConverter.toMailSendResponse("메일 전송에 성공했습니다.", true); } + @Transactional + public MailSend integratedProcee(String email) { + String code = makeVerificationCode(); + return sendVerificationEmail(email, code); + } + public String getVerificationMessage(String verificationCode) { StringBuffer verificationMessage = new StringBuffer(); verificationMessage