[Spring Boot] Ggmail로 인증 링크를 포함한 메일 보내기
https://sdy-study.tistory.com/269
Spring Boot 로 이메일 회원가입 하기
토이 프로젝트를 하다가 회원가입과 로그인, 로그아웃 기능을 만들게 되었는데 그때 사용했던 개념들을 정리하고자 한다 먼저 이메일을 통해서 회원가입을 했던 과정을 정리한다 - 개발환경 spr
sdy-study.tistory.com
위 글을 참고하여 재사용 가능하도록 커스텀해서 작성하였다.
1. Gmail SMTP 설정
예전에는 보안 수준 낮은 앱의 액세스 허용만 체크하면 됐지만
최근에는 정책이 바뀌어 2차 인증을 해주고 앱 비밀번호를 기억해둬야 한다.
https://hyunmin1906.tistory.com/276
[Go] Google Gmail SMTP 설정 방법 및 메일 전송
■ SMTP 간이 우편 전송 프로토콜(Simple Mail Transfer Protocol)의 약자. 이메일 전송에 사용되는 네트워크 프로토콜이다. 인터넷에서 메일 전송에 사용되는 표준이다. 1982년 RFC821에서 ..
hyunmin1906.tistory.com
2. gradle, yml 설정
build.gradle
//SMTP
implementation 'org.springframework.boot:spring-boot-starter-mail'
//thymeleaf
implementation 'org.springframework.boot:spring-boot-starter-thymeleaf'
application.yml
spring:
mail:
host: smtp.gmail.com
port: 587
username: 이메일주소
password: 앱비밀번호
properties:
mail:
smtp:
starttls:
enable: true
requred: true
auth: true
connectiontimeout: 5000
timeout: 5000
writetimeout: 5000
3. 클래스 작성(메일 전송)
위에 작성한 출처 블로그에서 작성한 코드를 기본으로 작성했고,
난 이메일 인증을 다시 사용할 예정이기 때문에
재사용 가능한 소스로 약간 커스텀했다.
회원가입 소스는 작성돼있다고 가정
- EmailToken
토큰id, user id, 만료여부 저장
import java.time.LocalDateTime;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import org.hibernate.annotations.GenericGenerator;
import lombok.AccessLevel;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.ToString;
@ToString
@Entity
@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class EmailToken {
private static final long EMAIL_TOKEN_EXPIRATION_TIME_VALUE = 5L;
@Id
@GeneratedValue(generator = "uuid2")
@GenericGenerator(name = "uuid2", strategy = "uuid2")
@Column(length = 36)
private String id;
private LocalDateTime expirationDate;
private boolean expired;
private String mbrNo;
public static EmailToken createEmailToken(String mbrNo) {
EmailToken emailToken = new EmailToken();
emailToken.expirationDate = LocalDateTime.now().plusMinutes(EMAIL_TOKEN_EXPIRATION_TIME_VALUE);
emailToken.expired = false;
emailToken.mbrNo = mbrNo;
return emailToken;
}
public void setTokenToUsed() {
this.expired = true;
}
}
- EmailTokenService
토큰 생성, 토큰 확인
import java.time.LocalDateTime;
import java.util.Optional;
import org.springframework.stereotype.Service;
import org.springframework.util.Assert;
import com.pigplace.comn.entity.EmailToken;
import com.pigplace.comn.repository.EmailTokenRepository;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
@Slf4j
@RequiredArgsConstructor
@Service
public class EmailTokenService {
private final EmailTokenRepository emailTokenRepository;
// 이메일 인증 토큰 생성
public EmailToken createEmailToken(String mbrNo, String receiverEmail) {
Assert.notNull(mbrNo, "mbrNo는 필수입니다");
Assert.hasText(receiverEmail, "receiverEmail은 필수입니다.");
// 이메일 토큰 저장
EmailToken emailToken = EmailToken.createEmailToken(mbrNo);
System.out.println("EmailToken >> " + emailToken);
emailTokenRepository.save(emailToken);
return emailToken;
}
// 유효한 토큰 가져오기
public EmailToken findByIdAndExpirationDateAfterAndExpired(String emailTokenId) throws Exception {
Optional<EmailToken> emailToken = emailTokenRepository
.findByIdAndExpirationDateAfterAndExpired(emailTokenId, LocalDateTime.now(), false);
// 토큰이 없다면 예외 발생
return emailToken.orElseThrow(() -> new Exception("BaseResponseStatus.DATABASE_ERROR"));
}
}
- MailConentBuilder
메일 content 생성
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.thymeleaf.TemplateEngine;
import org.thymeleaf.context.Context;
import lombok.RequiredArgsConstructor;
@Service
@RequiredArgsConstructor
public class MailContentBuilder {
private final TemplateEngine templateEngine;
@Value("${server.url}")
private String serverUrl;
public String signupBuild(String tokenId) {
Context context = new Context();
context.setVariable("link", "http://"+ serverUrl +"/member/confirm-email/" + tokenId);
return templateEngine.process("joinMail", context);
}
}
- joinMail.html
MailContentBuilder 에서 생성한 link 를 a 태그 속성에 추가한다.
<!DOCTYPE html>
<html lang="ko" xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>이메일 인증 링크</title>
</head>
<body>
<span>계정 활성화를 위해서 아래의 인증 링크를 클릭해주세요.</span><br>
<a th:href="${link}">인증 링크</a>
</body>
</html>
EmailSenderService
메일을 보내는 서비스
import org.springframework.mail.MailException;
import org.springframework.mail.javamail.JavaMailSender;
import org.springframework.mail.javamail.MimeMessageHelper;
import org.springframework.mail.javamail.MimeMessagePreparator;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import com.pigplace.common.support.PigException;
import com.pigplace.member.vo.EmailVO;
import lombok.RequiredArgsConstructor;
@Service
@RequiredArgsConstructor
public class EmailSenderService {
private final JavaMailSender javaMailSender;
@Async
public void sendEmail(EmailVO emailVO) throws Exception {
MimeMessagePreparator messagePreparator = mimeMessage -> {
MimeMessageHelper messageHelper = new MimeMessageHelper(mimeMessage, true, "UTF-8");
// messageHelper.setFrom("보내는 사람 이메일 ");
messageHelper.setTo(emailVO.getReceiverEmail());
messageHelper.setSubject(emailVO.getSubject());
messageHelper.setText(emailVO.getText(), true);
System.out.println("##############"+emailVO.getText());
};
try {
javaMailSender.send(messagePreparator);
// log.info("활성화 메일이 보내졌다");
} catch (MailException e) {
// log.error(String.valueOf(e));
e.printStackTrace();
throw new PigException("메일을 여기로 보내는 중 에러 발생 : " + emailVO.getReceiverEmail());
}
}
}
- JoinController
MailSenderService를 호출하는 컨트롤러.
회원가입 요청과 동시에 인증 요청 메일 보내기
회원가입하는 controller에 작성하였다.
//토큰 생성
EmailToken emailToken = emailTokenService.createEmailToken(user.getMbrNo(), user.getUserId());
String message = mailContentBuilder.signupBuild(emailToken.getId());
EmailVO emailVO = new EmailVO();
emailVO.setReceiverEmail(user.getUserId());
emailVO.setSubject("PigSpace가입을 환영합니다.");
emailVO.setText(message);
메일을 보내면
인증 링크를 포함한 메일이 전송된다.
css는 전혀 없어서 나중에 꾸밀 예정
- EmailController
위에서 전송한 메일에서 인증 링크를 클릭 했을 때
토큰을 확인하는 소스
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.pigplace.common.support.ControllerSupport;
import com.pigplace.common.support.ResponseEntity;
import com.pigplace.comn.service.EmailService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
@Slf4j
@Validated
@RequiredArgsConstructor
@RestController
@RequestMapping("/member")
public class EmailController extends ControllerSupport{
private final EmailService emailService;
@GetMapping("/confirm-email/{tokenId}")
public ResponseEntity<?> viewConfirmEmail(@PathVariable("tokenId") String tokenId) {
try {
boolean result = emailService.verifyEmail(tokenId);
// return new BaseResponse<>(result);
} catch (Exception exception) {
// return new BaseResponse<>(exception.getStatus());
}
return getOkResponse();
}
}