일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | |||
5 | 6 | 7 | 8 | 9 | 10 | 11 |
12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 | 20 | 21 | 22 | 23 | 24 | 25 |
26 | 27 | 28 | 29 | 30 | 31 |
- ORM
- superBuilder
- Vue
- IntelliJ
- CI/CD
- 알고리즘
- docker
- hibernate
- cd
- java
- 해시맵
- Di
- CI
- 뷰
- dabase
- map
- JPA
- builder-pattern
- CKA
- 프로그래머스
- programmers
- Kubernetes
- Spring
- Oracle
- DevOps
- SpringMVC
- 코딩테스트연습
- 자바
- k8s
- vuejs
- Today
- Total
문홍의 공부장
[Spring/Java] 회원가입 이메일 인증 구현하기 본문
많은 사이트에서 회원가입 시 이메일 인증을 요구하고 있다.
이메일로 인증번호를 발송하여 해당 인증번호를 입력하게 하는 경우도 있지만, 회원가입 시 인증 메일을 보내, 해당 메일에서 링크를 클릭하여 인증 및 회원가입을 완료하는 경우도 있다.
이전 팀 프로젝트에서 전자의 방법으로 이메일 인증을 구현해 보았으니, 이번 개인 프로젝트에서는 후자의 방법으로 구현하고, 그 내용을 정리해보려고 한다.
큰 틀을 동일하나, 인증키, 인증상태의 DB 저장 여부에 따라 약간의 차이가 있다. 인증상태를 어디에서 관리하느냐(프론트/백) 에 따라 DB 저장 여부가 결정된다고 볼 수 있다. (3-1 방법으로 했을 때에는 인증에 관한 데이터는 DB에 저장하지 않고, 유효성 검사를 통해 걸러냈다.)
1. 메일 인증 관련 라이브러리 다운받기(의존성 주입)
2. 설정파일(Configuration.java 혹은 .xml)에 bean 설정
3-1. 인증번호를 발송하는 경우
- 인증번호(인증키) 발송 후, 해당 인증번호를 쿠키에 저장해 두었다가 제한 시간(ex 03:00) 내에 옳게 입력할 경우 유효성 검사 통과. 그렇지 않을 경우 인증번호 재발송 필요 (DB에 인증키(authKey)를 저장하지 않음)
3-2. 메일 인증 링크를 통하는 경우
- '회원가입' 버튼 클릭 시, DB에 기본정보 & 인증키(authKey) 를 저장 (authStatus = default 0)
- 메일 링크 클릭 시 파라미터로 전달받은 데이터(email, authKey) 가 일치할 경우 authStatus = 1 으로 업데이트 (인증 완료 & 회원가입 최종 성공)
- 로그인 시 authStatus != 1 일 경우 인증되지 않은 계정이므로 로그인 제한
4. 완료!
1. 메일 인증 관련 라이브러리 다운받기(의존성 주입)
pom.xml
<!-- 이메일 인증: https://mvnrepository.com/artifact/javax.mail/mail -->
<dependency>
<groupId>javax.mail</groupId>
<artifactId>mail</artifactId>
<version>1.4.7</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework/spring-context-support -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context-support</artifactId>
<version>5.2.0.RELEASE</version>
</dependency>
2. 설정파일(Configuration.java 혹은 .xml)에 bean 설정
email-context.xml
이전에 Configuration.java 파일로 설정해보았으니, 이번엔 email-context.xml 파일을 만들어 빈 설정을 해 보았다.
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<!-- email 인증 관련 -->
<bean id="mailSender" class="org.springframework.mail.javamail.JavaMailSenderImpl">
<property name="host" value="smtp.gmail.com" />
<property name="port" value="587" />
<property name="username" value="이메일 주소"/>
<property name="password" value="비밀번호" />
<property name="javaMailProperties">
<props>
<prop key="mail.transport.protocol">smtp</prop>
<prop key="mail.smtp.auth">true</prop>
<prop key="mail.smtp.starttls.enable">true</prop>
<prop key="mail.debug">true</prop>
</props>
</property>
</bean>
</beans>
(참고) 아래는 Configuration.java 파일로 빈 설정하였을 시.
@Configuration
public class MailAuthConfiguration {
@Bean(name="mailSender")
public JavaMailSender getJavaMailSender() {
Properties properties = new Properties();
properties.put("mail.smtp.auth", true);
properties.put("mail.transport.protocol", "smtp");
properties.put("mail.smtp.starttls.enable", true);
properties.put("mail.smtp.starttls.required", true);
properties.put("mail.debug", true);
JavaMailSenderImpl mailSender = new JavaMailSenderImpl();
mailSender.setHost("smtp.gmail.com");
mailSender.setPort(587);
mailSender.setUsername("이메일 주소");
mailSender.setPassword("비밀번호");
mailSender.setDefaultEncoding("utf-8");
mailSender.setJavaMailProperties(properties);
return mailSender;
}
}
메일 인증 링크를 통하는 경우 : '회원가입' 버튼 클릭 시,
3-1. DB에 기본정보 저장
MemberController.java - signUp (회원가입)
@RestController
public class MemberRController {
@Autowired
private MemberService memberService;
@Autowired
private MailSendService mss;
@RequestMapping("/member/signUp")
public void signUp(@ModelAttribute MemberDTO memberDTO){
// DB에 기본정보 insert
memberService.signUp(memberDTO);
//임의의 authKey 생성 & 이메일 발송
String authKey = mss.sendAuthMail(memberDTO.getEmail());
memberDTO.setAuthKey(authKey);
Map<String, String> map = new HashMap<String, String>();
map.put("email", memberDTO.getEmail());
map.put("authKey", memberDTO.getAuthKey());
System.out.println(map);
//DB에 authKey 업데이트
memberService.updateAuthKey(map);
}
}
3-2. 인증키 생성 & 이메일 발송
MailSendService.java
@Service("mss")
public class MailSendService {
@Autowired
private JavaMailSenderImpl mailSender;
//인증키 생성
private String getKey(int size) {
this.size = size;
return getAuthCode();
}
//인증코드 난수 발생
private String getAuthCode() {
Random random = new Random();
StringBuffer buffer = new StringBuffer();
int num = 0;
while(buffer.length() < size) {
num = random.nextInt(10);
buffer.append(num);
}
return buffer.toString();
}
//인증메일 보내기
public String sendAuthMail(String email) {
//6자리 난수 인증번호 생성
String authKey = getKey(6);
//인증메일 보내기
try {
MailUtils sendMail = new MailUtils(mailSender);
sendMail.setSubject("회원가입 이메일 인증");
sendMail.setText(new StringBuffer().append("<h1>[이메일 인증]</h1>")
.append("<p>아래 링크를 클릭하시면 이메일 인증이 완료됩니다.</p>")
.append("<a href='http://localhost:9080/member/signUpConfirm?email=")
.append(email)
.append("&authKey=")
.append(authKey)
.append("' target='_blenk'>이메일 인증 확인</a>")
.toString());
sendMail.setFrom("이메일 주소", "관리자");
sendMail.setTo(email);
sendMail.send();
} catch (MessagingException e) {
e.printStackTrace();
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
return authKey;
}
}
MailUtils 는 이메일 전송을 위한 메세지 메일 발송, 메세지 설정 등을 위해 만든 클래스이다. 클래스를 따로 만들지 않고, 바로 MimeMessasge 클래스를 사용하여 메일 발송 및 메세지를 구성하여도 무방하다.
MailUtils.java
public class MailUtils {
private JavaMailSender mailSender;
private MimeMessage message;
private MimeMessageHelper messageHelper;
public MailUtils(JavaMailSender mailSender) throws MessagingException {
this.mailSender = mailSender;
message = this.mailSender.createMimeMessage();
messageHelper = new MimeMessageHelper(message, true, "UTF-8");
}
public void setSubject(String subject) throws MessagingException {
messageHelper.setSubject(subject);
}
public void setText(String htmlContent) throws MessagingException {
messageHelper.setText(htmlContent, true);
}
public void setFrom(String email, String name) throws UnsupportedEncodingException, MessagingException {
messageHelper.setFrom(email, name);
}
public void setTo(String email) throws MessagingException {
messageHelper.setTo(email);
}
public void addInline(String contentId, DataSource dataSource) throws MessagingException {
messageHelper.addInline(contentId, dataSource);
}
public void send() {
mailSender.send(message);
}
}
(참고) MimeMessasge 클래스를 사용하여 메일 구성할 경우
MimeMessage mail = mailSender.createMimeMessage();
String mailContent = "<h1>[이메일 인증]</h1><br><p>아래 링크를 클릭하시면 이메일 인증이 완료됩니다.</p>"
+ "<a href='http://localhost:9080/member/signUpConfirm?email="
+ email + "&authKey=" + authKey + "' target='_blenk'>이메일 인증 확인</a>";
try {
mail.setSubject("회원가입 이메일 인증 ", "utf-8");
mail.setText(mailContent, "utf-8", "html");
mail.addRecipient(Message.RecipientType.TO, new InternetAddress(email));
mailSender.send(mail);
} catch (MessagingException e) {
e.printStackTrace();
}
위 단계까지 성공적으로 진행되었다면, 파라미터로 받은 이메일 주소로 메일이 와 있을 것이다.
아! 그 전에, Gmail의 보안 수준(2단계 인증) 을 낮춰 놓아야 접근이 가능하다. 그렇지 않으면 아래와 같은 인증 에러 메세지가 뜰 것이다.
AuthenticationFailedException: Username and Password not accepted
방법은 간단하다. 아래 링크에 들어가서 [보안 수준이 낮은 앱 허용: 사용] 으로 설정을 변경해 놓으면 된다. (보안 수준을 낮추는 것이기 때문에 당연히! 평상시에는 권장하지 않는다.)
https://myaccount.google.com/lesssecureapps
자, 이제 진짜로 이메일이 보내졌을 것이다. [이메일 인증 확인] 링크(/member/signUpConfirm)를 클릭하여 인증을 마무리한다.
3-3. 파라미터로 전달받은 데이터(authKey) 가 일치할 경우 authStatus = 1 으로 업데이트 (인증 완료 & 회원가입 최종 성공)
MemberController.java - signUpConfirm
@GetMapping("/member/signUpConfirm")
public ModelAndView signUpConfirm(@RequestParam Map<String, String> map, ModelAndView mav){
//email, authKey 가 일치할경우 authStatus 업데이트
memberService.updateAuthStatus(map);
mav.addObject("display", "/view/member/signUp_confirm.jsp");
mav.setViewName("/view/index");
return mav;
}
4. 로그인 시 authStatus != 1 일 경우(인증되지 않은 계정) 로그인 제한
SELECT * FROM MEMBER WHERE EMAIL = #{email} AND PWD = #{pwd} AND AUTHSTATUS = 1
'개발 > Spring' 카테고리의 다른 글
[Spring] ResponseBodyAdvice 를 이용하여 ControllerAdvice 활용하기 (1) | 2024.01.24 |
---|---|
@Primary 와 @Qualifier (2) - 활용편: 2개 이상의 데이터베이스 연결하기 (0) | 2023.05.31 |
[Spring] @Primary 와 @Qualifier 비교하고 이해하기 (0) | 2023.04.18 |
[Spring] 스프링 핵심 개념: Ioc(제어의 역전)과 DI(의존성 주입) (0) | 2020.02.07 |
[Spring] Spring Framework의 정의와 특징 (0) | 2020.02.07 |