forked from Me1tingPot/BE
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
bb2295d
commit 55779b5
Showing
13 changed files
with
382 additions
and
5 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
82 changes: 82 additions & 0 deletions
82
src/main/java/meltingpot/server/auth/controller/MailController.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,82 @@ | ||
package meltingpot.server.auth.controller; | ||
|
||
import io.swagger.v3.oas.annotations.Operation; | ||
import io.swagger.v3.oas.annotations.responses.ApiResponse; | ||
import io.swagger.v3.oas.annotations.responses.ApiResponses; | ||
import jakarta.validation.Valid; | ||
import lombok.RequiredArgsConstructor; | ||
import meltingpot.server.auth.controller.dto.MailVerificationRequestDto; | ||
import meltingpot.server.auth.controller.dto.VerificationCodeRequestDto; | ||
import meltingpot.server.auth.service.MailService; | ||
import meltingpot.server.exception.DuplicateException; | ||
import meltingpot.server.exception.MailVerificationException; | ||
import meltingpot.server.util.ResponseCode; | ||
import meltingpot.server.util.ResponseData; | ||
import org.slf4j.Logger; | ||
import org.slf4j.LoggerFactory; | ||
import org.springframework.http.ResponseEntity; | ||
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; | ||
|
||
@RequiredArgsConstructor | ||
@RestController | ||
@RequestMapping("/api/v1/mail") | ||
public class MailController { | ||
private final MailService mailService; | ||
private final Logger logger = LoggerFactory.getLogger(this.getClass()); | ||
|
||
// 정회원 인증 메일 전송 | ||
@PostMapping("") | ||
@Operation(summary="이메일 인증번호 전송 [임시]", description="[이메일 인증 임시 구현 SMTP]\n 입력 받은 이메일로 인증 번호를 전송합니다.") | ||
@ApiResponses(value = { | ||
@ApiResponse(responseCode = "MAIL_VERIFICATION_SEND_SUCCESS", description = "이메일 인증번호 전송 성공"), | ||
@ApiResponse(responseCode = "VERIFICATION_CODE_ALREADY_EXIST", description = "이미 생성한 인증 번호가 있습니다"), | ||
@ApiResponse(responseCode = "MAIL_SEND_FAIL", description = "이메일 전송 실패") | ||
}) | ||
public ResponseEntity<ResponseData> sendVerificationMail( | ||
@RequestBody @Valid MailVerificationRequestDto request | ||
) { | ||
try{ | ||
logger.info("MAIL_VERIFICATION_SEND_SUCCESS (200 OK)"); | ||
return ResponseData.toResponseEntity(mailService.sendVerificationMail(request)); | ||
|
||
}catch(MailVerificationException e){ | ||
return ResponseData.toResponseEntity(e.getResponseCode()); | ||
} | ||
} | ||
|
||
@PostMapping("verification") | ||
@Operation(summary="이메일 인증번호 확인", description="이메일 인증 번호를 입력 받고 올바른 번호인지 확인합니다." ) | ||
@ApiResponses(value = { | ||
@ApiResponse(responseCode = "MAIL_VERIFICATION_CHECK_SUCCESS", description = "인증번호가 일치합니다"), | ||
@ApiResponse(responseCode = "AUTHENTICATION_NOT_FOUND", description = "메일 인증 정보를 찾을 수 없습니다"), | ||
@ApiResponse(responseCode = "AUTH_TIME_OUT", description = "인증 시간을 초과했습니다"), | ||
@ApiResponse(responseCode = "AUTH_NUMBER_INCORRECT", description = "인증 번호가 틀렸습니다"), | ||
}) | ||
public ResponseEntity<ResponseData> checkVerification( | ||
@RequestBody @Valid VerificationCodeRequestDto request | ||
) { | ||
try{ | ||
logger.info("VERIFICATION_CHECK_SUCCESS (200 OK)"); | ||
return ResponseData.toResponseEntity( mailService.checkVerification(request)); | ||
|
||
}catch(MailVerificationException e){ | ||
return ResponseData.toResponseEntity(e.getResponseCode()); | ||
} | ||
} | ||
|
||
// 이메일 중복 확인 | ||
@PostMapping("duplication") | ||
@Operation(summary="이메일 중복 체크", description="이미 가입한 이메일인지 확인하는 API 입니다.\n" ) | ||
public ResponseEntity<ResponseData> checkEmail( | ||
@RequestBody @Valid MailVerificationRequestDto request) { | ||
try{ | ||
mailService.checkUserName(request.email()); | ||
return ResponseData.toResponseEntity(ResponseCode.MAIL_AVAILABLE); | ||
}catch (DuplicateException e){ | ||
return ResponseData.toResponseEntity(ResponseCode.EMAIL_DUPLICATION); | ||
} | ||
} | ||
} |
12 changes: 12 additions & 0 deletions
12
src/main/java/meltingpot/server/auth/controller/dto/MailVerificationRequestDto.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
package meltingpot.server.auth.controller.dto; | ||
|
||
import jakarta.validation.constraints.NotBlank; | ||
import jakarta.validation.constraints.Pattern; | ||
import meltingpot.server.util.VerificationUtil; | ||
|
||
public record MailVerificationRequestDto( | ||
@NotBlank(message = "email is required") | ||
@Pattern(regexp = VerificationUtil.USERNAME_REGEXP) | ||
String email | ||
) { | ||
} |
14 changes: 14 additions & 0 deletions
14
src/main/java/meltingpot/server/auth/controller/dto/VerificationCodeRequestDto.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
package meltingpot.server.auth.controller.dto; | ||
|
||
import jakarta.validation.constraints.NotBlank; | ||
import jakarta.validation.constraints.Pattern; | ||
import meltingpot.server.util.VerificationUtil; | ||
|
||
public record VerificationCodeRequestDto( | ||
@NotBlank(message = "email is required") | ||
@Pattern(regexp = VerificationUtil.USERNAME_REGEXP) | ||
String email, | ||
@NotBlank | ||
String code | ||
) { | ||
} |
121 changes: 121 additions & 0 deletions
121
src/main/java/meltingpot/server/auth/service/MailService.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,121 @@ | ||
package meltingpot.server.auth.service; | ||
|
||
import lombok.RequiredArgsConstructor; | ||
import meltingpot.server.auth.controller.dto.MailVerificationRequestDto; | ||
import meltingpot.server.auth.controller.dto.VerificationCodeRequestDto; | ||
import meltingpot.server.domain.entity.Constants; | ||
import meltingpot.server.domain.entity.MailVerification; | ||
import meltingpot.server.domain.repository.AccountRepository; | ||
import meltingpot.server.domain.repository.MailVerificationRepository; | ||
import meltingpot.server.exception.DuplicateException; | ||
import meltingpot.server.exception.MailVerificationException; | ||
import meltingpot.server.util.MailUtil; | ||
import meltingpot.server.util.ResponseCode; | ||
import org.springframework.stereotype.Service; | ||
import org.springframework.transaction.annotation.Transactional; | ||
|
||
import java.security.NoSuchAlgorithmException; | ||
import java.security.SecureRandom; | ||
import java.time.LocalDateTime; | ||
import java.util.Map; | ||
import java.util.Optional; | ||
import java.util.Random; | ||
|
||
@RequiredArgsConstructor | ||
@Service | ||
public class MailService { | ||
private final MailVerificationRepository mailVerificationRepository; | ||
private final MailUtil mailUtil; | ||
private final AccountRepository accountRepository; | ||
|
||
// 인증 코드 생성 | ||
private String createCode() { | ||
try { | ||
Random rand = SecureRandom.getInstanceStrong(); | ||
StringBuilder code = new StringBuilder(); | ||
|
||
for (int i = 0; i < 6; i++) { | ||
// 0~9까지 난수 생성 | ||
String num = Integer.toString(rand.nextInt(10)); | ||
code.append(num); | ||
} | ||
|
||
return code.toString(); | ||
|
||
} catch (NoSuchAlgorithmException e) { | ||
e.printStackTrace(); | ||
return null; | ||
} | ||
} | ||
|
||
// 인증 메일 발송 | ||
@Transactional | ||
public ResponseCode sendVerificationMail(MailVerificationRequestDto request) { | ||
String email = request.email(); | ||
|
||
// 이미 유효한 인증 정보가 있는 경우 | ||
Optional<MailVerification> oldMailVerification = mailVerificationRepository.findByEmailAndExpiredAtIsAfterNowAndVerifiedFalse(email,LocalDateTime.now()); | ||
if(oldMailVerification.isPresent()){ | ||
throw new MailVerificationException(ResponseCode.VERIFICATION_CODE_ALREADY_EXIST); | ||
} | ||
|
||
String code = createCode(); | ||
Map<String,String> mailValues = Map.of("code", code); | ||
|
||
String title = "[멜팅팟] 이메일 인증을 위한 인증 번호 안내"; | ||
|
||
// 메일 전송 | ||
mailUtil.sendMimeMessageMailWithValues(title, email, "EmailAuthenticationForm.html", mailValues); | ||
|
||
LocalDateTime expiredAt = LocalDateTime.now().plusMinutes(Constants.AUTH_TIME_LIMIT); | ||
|
||
MailVerification mailVerification = MailVerification.builder() | ||
.email(email) | ||
.expiredAt(expiredAt) | ||
.authenticationNumber(code) | ||
.build(); | ||
|
||
mailVerificationRepository.save(mailVerification); | ||
|
||
return ResponseCode.MAIL_VERIFICATION_SEND_SUCCESS; | ||
|
||
} | ||
|
||
|
||
// 인증 번호 확인 | ||
@Transactional | ||
public ResponseCode checkVerification(VerificationCodeRequestDto request) { | ||
|
||
// 현재 유효한 인증 정보 가져오기 | ||
MailVerification mailVerification = mailVerificationRepository.findByEmailAndExpiredAtIsAfterNowAndVerifiedFalse(request.email(), LocalDateTime.now()) | ||
.orElseThrow( // 인증 정보가 없는 경우 | ||
()->new MailVerificationException(ResponseCode.AUTHENTICATION_NOT_FOUND) | ||
); | ||
|
||
// 인증 번호 유효 기간을 초과한 경우 | ||
if(LocalDateTime.now().isAfter(mailVerification.getExpiredAt())){ | ||
throw new MailVerificationException(ResponseCode.AUTH_TIME_OUT); | ||
} | ||
|
||
// 인증 번호가 틀린 경우 | ||
if(!mailVerification.getAuthenticationNumber().equals(request.code())){ | ||
throw new MailVerificationException(ResponseCode.AUTH_NUMBER_INCORRECT); | ||
} | ||
|
||
// 인증에 성공한 경우: 제한 시간 내에 인증 번호를 올바르게 입력한 경우 | ||
mailVerification.setVerified(true); | ||
mailVerificationRepository.save(mailVerification); | ||
|
||
return ResponseCode.MAIL_VERIFICATION_CHECK_SUCCESS; | ||
|
||
} | ||
|
||
// 회원가입시 이메일 유효성 확인 | ||
@Transactional(readOnly = true) | ||
public void checkUserName(String username) { | ||
if(accountRepository.existsByUsername(username)){ | ||
throw new DuplicateException(ResponseCode.EMAIL_DUPLICATION); | ||
} | ||
} | ||
|
||
} |
16 changes: 16 additions & 0 deletions
16
src/main/java/meltingpot/server/domain/repository/MailVerificationRepository.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
package meltingpot.server.domain.repository; | ||
|
||
import meltingpot.server.domain.entity.MailVerification; | ||
import org.springframework.data.jpa.repository.JpaRepository; | ||
import org.springframework.data.jpa.repository.Query; | ||
|
||
import java.time.LocalDateTime; | ||
import java.util.Optional; | ||
|
||
public interface MailVerificationRepository extends JpaRepository<MailVerification, Long> { | ||
@Query(value = "select * from mail_verification where email = :email and expired_at >= :now and verified = false", nativeQuery = true) | ||
Optional<MailVerification> findByEmailAndExpiredAtIsAfterNowAndVerifiedFalse(String email, LocalDateTime now); | ||
|
||
boolean existsByEmailAndVerifiedTrue(String email); | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
12 changes: 12 additions & 0 deletions
12
src/main/java/meltingpot/server/exception/MailSendException.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
package meltingpot.server.exception; | ||
|
||
import lombok.AllArgsConstructor; | ||
import lombok.Getter; | ||
import lombok.RequiredArgsConstructor; | ||
import meltingpot.server.util.ResponseCode; | ||
|
||
@Getter | ||
@AllArgsConstructor | ||
public class MailSendException extends RuntimeException{ | ||
private final ResponseCode responseCode; | ||
} |
11 changes: 11 additions & 0 deletions
11
src/main/java/meltingpot/server/exception/MailVerificationException.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
package meltingpot.server.exception; | ||
import lombok.AllArgsConstructor; | ||
import lombok.Getter; | ||
import lombok.RequiredArgsConstructor; | ||
import meltingpot.server.util.ResponseCode; | ||
|
||
@Getter | ||
@AllArgsConstructor | ||
public class MailVerificationException extends RuntimeException{ | ||
private final ResponseCode responseCode; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,51 @@ | ||
package meltingpot.server.util; | ||
|
||
import jakarta.mail.MessagingException; | ||
import jakarta.mail.internet.MimeMessage; | ||
import lombok.RequiredArgsConstructor; | ||
import meltingpot.server.exception.MailSendException; | ||
import org.springframework.mail.MailException; | ||
import org.springframework.mail.javamail.JavaMailSender; | ||
import org.springframework.mail.javamail.MimeMessageHelper; | ||
import org.springframework.scheduling.annotation.Async; | ||
import org.springframework.stereotype.Component; | ||
import org.thymeleaf.context.Context; | ||
import org.thymeleaf.spring6.SpringTemplateEngine; | ||
|
||
import java.util.Map; | ||
|
||
@RequiredArgsConstructor | ||
@Component | ||
public class MailUtil { | ||
private final JavaMailSender javaMailSender; | ||
private final SpringTemplateEngine templateEngine; | ||
|
||
@Async | ||
public void sendMimeMessageMailWithValues(String title, String to, String templateName, Map<String, String> values) { | ||
try { | ||
MimeMessage message = javaMailSender.createMimeMessage(); | ||
MimeMessageHelper helper = new MimeMessageHelper(message, true); | ||
|
||
// 메일 제목 설정 | ||
helper.setSubject(title); | ||
|
||
// 수신자 설정 | ||
helper.setFrom("멜팅팟 <[email protected]>"); | ||
helper.setTo(to); | ||
|
||
// 템플릿에 전달할 데이터 설정 | ||
Context context = new Context(); | ||
values.forEach(context::setVariable); | ||
|
||
// 메일 내용 설정 : 템플릿 프로세스 | ||
String html = templateEngine.process(templateName, context); | ||
helper.setText(html, true); | ||
|
||
// 메일 보내기 | ||
javaMailSender.send(message); | ||
} catch (MessagingException | MailException e) { | ||
throw new MailSendException(ResponseCode.MAIL_SEND_FAIL); | ||
} | ||
} | ||
|
||
} |
Oops, something went wrong.