package com.randomchat.main.controller.register;
import com.randomchat.main.dto.register.EmailVerificationDTO;
import com.randomchat.main.service.email.EmailVerificationService;
import com.randomchat.main.service.register.RegisterService;
import jakarta.mail.MessagingException;
import lombok.RequiredArgsConstructor;
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;
import java.io.IOException;
@RequiredArgsConstructor
@RestController
@RequestMapping("/register")
public class EmailVerificationController {
private final EmailVerificationService emailVerificationService;
private final RegisterService registerService;
@PostMapping("/email/verification")
public ResponseEntity<String> emailVerification(@RequestBody EmailVerificationDTO emailVerificationDTO) throws MessagingException, IOException {
String email = emailVerificationDTO.getEmail();
// 1. 이메일 인증이 들어온 이메일 정보로 가입된 계정이 있다면 deny
if(registerService.checkEmailDuplication(email)) return ResponseEntity.status(409).body("이미 가입된 이메일입니다.");
// 2. 이메일 인증이 들어온 기준 시간으로 부터 5분 내로 DB 내에 5회 인증 요청이 있다면 deny
// emailVerificationService.esExceededLimit(email);
// 3. 이메일 인증용 난수 생성
String verificationCode = emailVerificationService.createCode();
// 4. 이메일을 발송한 뒤 이메일 인증 데이터 DB 에 저장
emailVerificationService.sendEmail(email, verificationCode); // 인증 이메일 발송
emailVerificationService.saveEmailVerification(email, verificationCode); // DB 에 인증 내용 저장
return ResponseEntity.ok("이메일이 발송되었습니다.");
}
}
2. RegisterService ( JPA 쿼리 메소드를 사용하여 동일한 메일이 있는지 체크 )
package com.randomchat.main.service.register;
import com.randomchat.main.domain.users.Users;
import com.randomchat.main.dto.register.RegisterDTO;
import com.randomchat.main.repository.UsersRepository;
import lombok.RequiredArgsConstructor;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Service;
@Service
@RequiredArgsConstructor
public class RegisterService {
private final UsersRepository usersRepository;
private final BCryptPasswordEncoder bCryptPasswordEncoder;
public void register(RegisterDTO registerDTO) {
String email = registerDTO.getEmail();
String password = registerDTO.getPassword();
// 받아온 비밀번호 정보를 암호화
registerDTO.setPassword(bCryptPasswordEncoder.encode(registerDTO.getPassword()));
Users users = new Users();
Users user = users.createUser(registerDTO);
usersRepository.save(user);
}
public boolean checkEmailDuplication(String email) {
return usersRepository.existsByEmail(email);
}
public boolean checkNicknameDuplication(String nickname) {
return usersRepository.existsByNickname(nickname);
}
}
3. emailVerificationService
- createCode 메소드 -
1) 난수에 사용될 문자들을 미리 선언 후 RANDOM 클래스의 nextInt 메소드를 사용하여 문자 길이만큼의 수 내에서 랜덤 숫자를 생성
2) 랜덤 숫자를 다시 CHARACTERS 문자열의 인덱스로 접근하여 랜덤한 문자를 뽑아 StringBuilder 에 추가한 뒤 반복문이 다 돌면 빌드
- renderJspToString 메소드 -
1) templates 디렉토리에 있는 jsp 파일을 불러와 Resource 객체로 생성
2) 생성된 객체의 정보를 UTF-8 으로 인코딩된 String 으로 변환
3) String 으로 변환된 내용 중 ${verificationCode} 문자를 실제 인증 코드로 변경
- sendEmail 메소드 -
1) 이메일을 전송하기 위해 Mimemessage 객체를 생성
2) Mimemessage 를 쉽게 설정하도록 도와주는 헬퍼 클래스인 MimemessageHelper 클래스를 선언하여 다음과 같이 설정
true : HTML 형식을 지원하도록 설정
UTF-8 : 이메일 내용의 인코딩 방식을 설정
3) 헬퍼 플래스를 사용하여 이메일 주소, 제목, 내용을 다음과 같이 설정
setTo : 수신자 이메일 작성
setSubject : 메일 제목 설정
setText : 메일 본문을 설정, true 로 HTML 형식 적용
setFrom : 발신자의 이메일 주소를 설정
package com.randomchat.main.service.email;
import com.randomchat.main.domain.email.EmailVerification;
import com.randomchat.main.repository.EmailVerificationRepository;
import com.randomchat.main.repository.UsersRepository;
import jakarta.mail.MessagingException;
import jakarta.mail.internet.MimeMessage;
import lombok.RequiredArgsConstructor;
import org.springframework.core.io.Resource;
import org.springframework.core.io.ResourceLoader;
import org.springframework.mail.javamail.JavaMailSender;
import org.springframework.mail.javamail.MimeMessageHelper;
import org.springframework.stereotype.Service;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.security.SecureRandom;
import java.util.Random;
@Service
@RequiredArgsConstructor
public class EmailVerificationService {
private final UsersRepository usersRepository;
private final EmailVerificationRepository emailVerificationRepository;
private final ResourceLoader resourceLoader;
private final JavaMailSender mailSender;
private static final Random RANDOM = new SecureRandom();
private static final String CHARACTERS = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; // 난수에 사용될 문자
public String createCode() {
StringBuilder stringBuilder = new StringBuilder();
// 10 자리 난수를 생성할 것이므로 10회 반복
for (int i = 0; i < 10; i++) {
// 난수에 사용될 문자의 길이만큼의 랜덤한 값을 추출
int randomIndex = RANDOM.nextInt(CHARACTERS.length());
// 랜덤한 index 의 문자를 추출하여 문자열로 추가
stringBuilder.append(CHARACTERS.charAt(randomIndex));
}
return stringBuilder.toString();
}
public void saveEmailVerification(String email, String verificationCode) {
EmailVerification emailVerification = new EmailVerification();
EmailVerification createEmailVerification = emailVerification.createEmailVerification(email, verificationCode);
emailVerificationRepository.save(createEmailVerification);
}
public String renderJspToString(String verificationCode) throws IOException {
// JSP 파일 내용을 String 으로 읽어오기
Resource resource = resourceLoader.getResource("classpath:/templates/emailForm.jsp");
String content = new String(Files.readAllBytes(resource.getFile().toPath()), StandardCharsets.UTF_8);
// 인증 코드를 내용에 삽입하여 리턴
return content.replace("${verificationCode}", verificationCode);
}
public void sendEmail(String email, String verificationCode) throws IOException, MessagingException {
String emailContent = renderJspToString(verificationCode);
MimeMessage message = mailSender.createMimeMessage();
MimeMessageHelper helper = new MimeMessageHelper(message, true, "UTF-8");
helper.setTo(email);
helper.setSubject("랜덤 채팅의 이메일 인증 번호입니다.");
helper.setText(emailContent, true); // HTML 형식으로 보낼 때 true 설정
helper.setFrom("youngho3358@gmail.com");
mailSender.send(message);
}
}