[Spring] 이메일 인증 번호 전송, 유효 시간 (Gmail SMTP Server, Redis)
많이 부족해서 이곳저곳 참고하면서 구현해보았습니다.. 더 좋은 방법이나 조언 및 참고 링크 구합니다..!
○ 개발 환경
- intellij IDEA 2020.03
- spring boot 2.4.8
- project SDK : java 11
- gradle 6.9
- h2 database
- Redis
이메일을 통한 인증 방법에는 대부분 두 가지 형식이 있다.
1. 이메일로 인증번호를 전송한 후, 입력하여 인증
2. 이메일로 보낸 링크에 접속하여 인증
아무래도 사용자 입장에서는 2번이 그나마 편리하지 않을까라는 생각에 2번을 구현해보려 그랬는데
프로젝트를 1번으로 진행하기로 해서 1번으로!
1. 의존성
이메일 발송을 위해서 spring-boot-starter-mail 의존성 추가
build.gradle
implementation 'org.springframework.boot:spring-boot-starter-mail'
인증 번호 유효 시간 동안 저장을 위해서 spring-boot-starter-data-redis 의존성 추가
implementation 'org.springframework.boot:spring-boot-starter-data-redis'
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-data-redis'
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
implementation 'org.springframework.boot:spring-boot-starter-mail'
implementation 'org.springframework.boot:spring-boot-starter-validation'
implementation 'org.springframework.boot:spring-boot-starter-web'
compileOnly 'org.projectlombok:lombok'
developmentOnly 'org.springframework.boot:spring-boot-devtools'
runtimeOnly 'com.h2database:h2'
annotationProcessor 'org.projectlombok:lombok'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
testImplementation 'org.springframework.restdocs:spring-restdocs-mockmvc'
}
그 외 의존성은 이렇게 담았습니다.
2. Gmail 설정
Gmail의 보안 수준(2단계 인증)을 낮게 해 두어야 접근이 가능하며, 설정해두지 않으면 다음과 같은 인증 에러 메시지가 발생할 수 있다고 한다.
" AuthenticationFailedException: Username and Password not accepted "
따라서, 아래 링크를 통해 보안 수준이 낮은 앱의 허용 : 사용을 허용해준다.
https://www.google.com/settings/security/lesssecureapps
3. Gmail SMTP Server 설정
SMTP란 Simple Mail Transfer Protocol의 약자로, 전자 메일 전송을 위한 표준 프로토콜이다.
사용해볼 Gmail SMTP Server는 스팸 메일 방지를 위해 하루(24시간)에 최대 100개의 메일을 전송할 수 있도록 전송 제한이 있다. 다른 이메일들도 한도가 정해져 있으니 확인해 볼 것!
(이렇게 메일 발송을 위해서 자체적으로 구축하거나 제공되는 smtp를 사용하는 것 같다. 아마 100개 이상을 전송하기 위해서는 유료 기능을 이용하거나.. 음.. 다른 메일 서버를 이용할 것 같다.)
Gmail SMTP Server에 알맞은 설정을 다음과 같이 해줍니다. (https://www.siteground.com/kb/gmail-smtp-server/)
application.yml
spring:
mail:
host: smtp.gmail.com
port: 587
username: 이메일주소@gmail.com
password: 비밀번호
properties:
mail.smtp.auth: true
mail.smtp.starttls.enable: true
Gmail 외에도 다른 SMTP 서버(Naver, Daum)를 사용하려면 해당 서비스에 알맞은 설정을 해주어야 한다.
(이메일 형식, 포트 번호 등등 확인하여 설정해주기 / gitignore 하여 계정 정보 보호해주기)
4. Redis, H2 Database 설정
Window 10, Redis 설치 (https://github.com/microsoftarchive/redis/releases)
Redis를 이번 기회에 설치해보았는데,,
Redis(REmote Dictionary Server)는 NOSQL, 비 관계형 데이터베이스이다. KEY와 VALUE 구조로 이루어진 데이터 관리 시스템이다. 처리가 빠르고 유효 시간을 정해 데이터가 남지 않도록 해주려고 사용하였다.
application.yml
datasource:
url: jdbc:h2:tcp://localhost/~/test
username: sa
password:
driver-class-name: org.h2.Driver
redis:
host: 127.0.0.1
port: 6379
spring:
mail:
host: smtp.gmail.com
port: 587
username: 이메일주소@gmail.com
password: 비밀번호
properties:
mail.smtp.auth: true
mail.smtp.starttls.enable: true
datasource:
url: jdbc:h2:tcp://localhost/~/test
username: sa
password:
driver-class-name: org.h2.Driver
redis:
host: 127.0.0.1
port: 6379
jpa:
hibernate:
ddl-auto: create
properties:
hibernate:
format_sql: true
logging.level:
org.hibernate.SQL: debug
이메일로 인증 번호 전송하기
controller/MemberController.java
/*
이메일 전송
*/
@PostMapping("/members/email")
public ResponseEntity<Void> authEmail(@RequestBody @Valid EmailRequest request) {
memberService.authEmail(request);
return ResponseEntity.ok().build();
}
@Data
public class EmailRequest {
@Email
@NotBlank(message = "이메일(필수)")
private String email;
}
service/impl/MemberServiceImpl.java
이메일 전송
○ 임의의 인증키 생성
○ 이메일 전송
private final JavaMailSender javaMailSender;
private final RedisUtil redisUtil;
/*
이메일 전송
*/
@Override
@Transactional
public void authEmail(EmailRequest request) {
// 임의의 authKey 생성
Random random = new Random();
String authKey = String.valueOf(random.nextInt(888888) + 111111); // 범위 : 111111 ~ 999999
// 이메일 발송
sendAuthEmail(request.getEmail(), authKey);
}
○ 이메일 전송 ( Redis에 인증 번호 저장[5분간 유효] )
private void sendAuthEmail(String email, String authKey) {
String subject = "제목";
String text = "회원 가입을 위한 인증번호는 " + authKey + "입니다. <br/>";
try {
MimeMessage mimeMessage = javaMailSender.createMimeMessage();
MimeMessageHelper helper = new MimeMessageHelper(mimeMessage, true, "utf-8");
helper.setTo(email);
helper.setSubject(subject);
helper.setText(text, true); //포함된 텍스트가 HTML이라는 의미로 true.
javaMailSender.send(mimeMessage);
} catch (MessagingException e) {
e.printStackTrace();
}
// 유효 시간(5분)동안 {email, authKey} 저장
redisUtil.setDataExpire(authKey, email, 60 * 5L);
}
service/impl/RedisUtil.java
Redis
○ setData : {key, value} 구조로 저장
○ getData : key 입력 시, value 리턴 / value 입력 시, key 리턴
○ setDataExpire : {key, value}가 특정 유효 시간 동안만 저장되도록 함
○ deleteData : Data 삭제
@Service
@RequiredArgsConstructor
public class RedisUtil {
private final StringRedisTemplate redisTemplate;
// key를 통해 value 리턴
public String getData(String key) {
ValueOperations<String, String> valueOperations = redisTemplate.opsForValue();
return valueOperations.get(key);
}
public void setData(String key, String value) {
ValueOperations<String, String> valueOperations = redisTemplate.opsForValue();
valueOperations.set(key, value);
}
// 유효 시간 동안 (key, value) 저장
public void setDataExpire(String key, String value, long duration) {
ValueOperations<String, String> valueOperations = redisTemplate.opsForValue();
Duration expireDuration = Duration.ofSeconds(duration);
valueOperations.set(key, value, expireDuration);
}
// 삭제
public void deleteData(String key) {
redisTemplate.delete(key);
}
}
[결과 화면]
PostMan을 이용하여 "/members/email"로 email을 담아서 Send 해주면,
이렇게 이메일을 받을 수 있다. 내용에는 인증번호가 담겨있다.
그럼 이제 Redis에 인증 번호가 잘 담겨 있는 지를 확인해주자면,
Redis를 설치한 폴더 안에 redis-cli.exe를 실행해준다. (ex : C:\Program Files\Redis )
이렇게 keys * 명령어를 입력해주면, 담긴 key들을 모두 조회해준다.
그리고 유효 시간 5분이 지나게 된 후 다시 조회하면,
저절로 사라지게 된다.