Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

✨ [Feat] 이메일 인증 #5

Merged
merged 1 commit into from
Sep 30, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@ src/main/resources/.env
*.bak
*.swo

src/main/resources/application.properties




Expand Down
4 changes: 4 additions & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,10 @@ dependencies {
compileOnly 'org.projectlombok:lombok'
annotationProcessor 'org.projectlombok:lombok'

// Spring mail
implementation 'org.springframework.boot:spring-boot-starter-mail'
implementation 'com.sun.mail:jakarta.mail:2.0.1'

// Testing
testImplementation 'org.springframework.boot:spring-boot-starter-test'
testImplementation 'org.springframework.security:spring-security-test'
Expand Down
76 changes: 76 additions & 0 deletions src/main/java/dongguk/osori/domain/user/EmailService.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
package dongguk.osori.domain.user;

import jakarta.mail.internet.MimeMessage;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.mail.javamail.JavaMailSender;
import org.springframework.stereotype.Service;

import java.util.concurrent.TimeUnit;

@Service
@RequiredArgsConstructor
public class EmailService {

private final JavaMailSender javaMailSender;
private final RedisTemplate<String, String> redisTemplate;

@Value("${spring.mail.username}")
private String senderEmail;

private static final long CODE_EXPIRE_MINUTES = 5; // 인증 코드 유효 시간 (5분)

// 인증 번호 생성
public static int createNumber() {
return (int)(Math.random() * 900000) + 100000; // 6자리 랜덤 숫자
}

// 이메일 작성
public MimeMessage createMail(String recipientEmail, int authCode) {
MimeMessage message = javaMailSender.createMimeMessage();

try {
message.setFrom(senderEmail);
message.setRecipients(MimeMessage.RecipientType.TO, recipientEmail);
message.setSubject("[Osori] 회원가입을 위한 이메일 인증");

// 이메일 본문
String body = "<h1>안녕하세요. Osori입니다.</h1>"
+ "<h3>회원가입을 위한 요청하신 인증 번호입니다.</h3>"
+ "<div align='center' style='border:1px solid black; font-family:verdana;'>"
+ "<h2>회원가입 인증 코드입니다.</h2>"
+ "<h1 style='color:blue'>" + authCode + "</h1>"
+ "</div>"
+ "<br><h3>감사합니다.</h3>";

message.setText(body, "UTF-8", "html");
} catch (Exception e) {
e.printStackTrace();
}

return message;
}

// 이메일 전송 및 Redis 저장
public void sendEmail(String recipientEmail) {
int authCode = createNumber();
MimeMessage message = createMail(recipientEmail, authCode);

// Redis에 인증 코드 저장, 5분 후 만료
ValueOperations<String, String> valueOperations = redisTemplate.opsForValue();
valueOperations.set(recipientEmail, String.valueOf(authCode), CODE_EXPIRE_MINUTES, TimeUnit.MINUTES);

// 이메일 전송
javaMailSender.send(message);
}

// 인증 코드 확인
public boolean verifyAuthCode(String email, String inputCode) {
ValueOperations<String, String> valueOperations = redisTemplate.opsForValue();
String storedCode = valueOperations.get(email);

return storedCode != null && storedCode.equals(inputCode);
}
}
37 changes: 35 additions & 2 deletions src/main/java/dongguk/osori/domain/user/UserController.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package dongguk.osori.domain.user;

import dongguk.osori.domain.user.dto.EmailVerificationDto;
import dongguk.osori.domain.user.dto.LoginRequestDto;
import dongguk.osori.domain.user.dto.SignupUserDto;
import dongguk.osori.domain.user.dto.UserProfileDto;
Expand All @@ -9,6 +10,8 @@
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

import java.util.Map;

@Slf4j
@RequiredArgsConstructor
@CrossOrigin(origins = "http://localhost:3000")
Expand All @@ -17,18 +20,48 @@
public class UserController {

private final UserService userService;
private final EmailService emailService;


// 이메일 인증 코드 전송
@PostMapping("/signup/send-email")
public ResponseEntity<String> sendSignupEmail(@RequestBody Map<String,String> request) {
String email = request.get("email"); // 이메일 주소 추출
if (email == null || email.isEmpty()) {
return ResponseEntity.status(400).body("이메일이 올바르지 않습니다.");
}
emailService.sendEmail(email);
return ResponseEntity.ok("인증 코드가 이메일로 전송되었습니다.");
}


// 이메일 인증 코드 확인
@PostMapping("/signup/verify-code")
public ResponseEntity<String> verifyEmailCode(@RequestBody EmailVerificationDto verificationDto) {
boolean isVerified = emailService.verifyAuthCode(verificationDto.getEmail(), verificationDto.getCode());

if (isVerified) {
return ResponseEntity.ok("이메일 인증이 완료되었습니다.");
} else {
return ResponseEntity.status(400).body("잘못된 인증 코드입니다.");
}
}


// 회원가입
@PostMapping("/signup")
public ResponseEntity<String> signup(@RequestBody SignupUserDto signupUserDto) {
try {
// 이메일 인증이 완료된 사용자만 회원가입 가능
if (!emailService.verifyAuthCode(signupUserDto.getEmail(), signupUserDto.getAuthCode())) {
return ResponseEntity.status(400).body("이메일 인증이 완료되지 않았습니다.");
}

userService.signup(signupUserDto);
return ResponseEntity.ok("회원가입이 완료되었습니다.");
} catch (IllegalArgumentException e) {
log.error("Signup failed: {}", e.getMessage());
return ResponseEntity.status(400).body(e.getMessage());
} catch (Exception e) {
log.error("Unexpected error during signup", e);
return ResponseEntity.status(500).body("서버 에러 발생. 나중에 다시 시도해주세요.");
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package dongguk.osori.domain.user.dto;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@NoArgsConstructor
@AllArgsConstructor
public class EmailVerificationDto {
private String email;
private String code;
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,5 @@ public class SignupUserDto {
private String nickname;
private String email;
private String password;
private String authCode;
}
14 changes: 14 additions & 0 deletions src/main/resources/application.properties
Original file line number Diff line number Diff line change
Expand Up @@ -24,3 +24,17 @@ server.servlet.session.cookie.name=JSESSIONID
server.servlet.session.cookie.secure=false
server.servlet.session.cookie.http-only=true
server.servlet.session.timeout=30m

# Gmail smtp
spring.mail.host=smtp.gmail.com
spring.mail.port=587
[email protected]
spring.mail.password=giybewxenbvecvqr
spring.mail.properties.mail.smtp.auth=true
spring.mail.properties.mail.smtp.starttls.enable=true
spring.mail.properties.mail.smtp.starttls.required=true
spring.mail.properties.mail.smtp.ssl.trust=smtp.gmail.com
spring.mail.properties.mail.smtp.connectiontimeout=5000
spring.mail.properties.mail.smtp.timeout=5000
spring.mail.properties.mail.smtp.writetimeout=5000

Loading