build.gradle

// 이메일 인증을 위한 설정
implementation 'org.springframework.boot:spring-boot-starter-mail'

 

EmailConfig

package com.cloneweek.hanghaebnb.util.email;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
import org.springframework.mail.javamail.JavaMailSender;
import org.springframework.mail.javamail.JavaMailSenderImpl;

import java.util.Properties;

@Configuration
@PropertySource("classpath:application-email.properties")
public class EmailConfig {

    @Value("${mail.smtp.port}")
    private int port;
    @Value("${mail.smtp.socketFactory.port}")
    private int socketPort;
    @Value("${mail.smtp.auth}")
    private boolean auth;
    @Value("${mail.smtp.starttls.enable}")
    private boolean starttls;
    @Value("${mail.smtp.starttls.required}")
    private boolean startlls_required;
    @Value("${mail.smtp.socketFactory.fallback}")
    private boolean fallback;
    @Value("${AdminMail.id}")
    private String id;
    @Value("${AdminMail.password}")
    private String password;

    @Bean
    public JavaMailSender javaMailService() {
        JavaMailSenderImpl javaMailSender = new JavaMailSenderImpl();
        javaMailSender.setHost("smtp.gmail.com");
        javaMailSender.setUsername(id);
        javaMailSender.setPassword(password);
        javaMailSender.setPort(port);
        javaMailSender.setJavaMailProperties(getMailProperties());
        javaMailSender.setDefaultEncoding("UTF-8");
        return javaMailSender;
    }
    private Properties getMailProperties()
    {
        Properties pt = new Properties();
        pt.put("mail.smtp.socketFactory.port", socketPort);
        pt.put("mail.smtp.auth", auth);
        pt.put("mail.smtp.starttls.enable", starttls);
        pt.put("mail.smtp.starttls.required", startlls_required);
        pt.put("mail.smtp.socketFactory.fallback",fallback);
        pt.put("mail.smtp.socketFactory.class", "javax.net.ssl.SSLSocketFactory");
        return pt;
    }
}

 

EmailController

package com.cloneweek.hanghaebnb.util.email;

import com.cloneweek.hanghaebnb.dto.RequestDto.DupliCheckDto;
import com.cloneweek.hanghaebnb.dto.ResponseDto.ResponseBoolDto;
import com.cloneweek.hanghaebnb.util.exception.StatusMsgCode;
import com.cloneweek.hanghaebnb.dto.ResponseDto.ResponseMsgDto;
import com.cloneweek.hanghaebnb.dto.RequestDto.SignupRequestDto;
import lombok.RequiredArgsConstructor;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

import javax.validation.Valid;
import java.util.Map;

import static com.cloneweek.hanghaebnb.util.exception.StatusMsgCode.EXIST_NICK;
import static com.cloneweek.hanghaebnb.util.exception.StatusMsgCode.NICKNAME;

@RestController
@RequiredArgsConstructor
@RequestMapping("/api/users/email")
public class EmailController {
    private final EmailService emailService;

    // 입력한 이메일로 인증코드 발송
    @PostMapping("/confirm")
    public ResponseEntity<?> emailConfirm(@RequestBody EmailConfirmDto emailConfirmDto) throws Exception {
        emailService.sendSimpleMessage(emailConfirmDto.getEmail());
        return ResponseEntity.ok(new ResponseMsgDto(StatusMsgCode.EMAIL_CONFIRM));
    }

    // 인증코드 발송받은 이메일과 인증번호 입력
    @PostMapping("/verifycode")
    public ResponseEntity<ResponseMsgDto> verifyCode(@RequestParam String issuedCode, String email) {
//        return emailService.verifyCode(issuedCode, email);
        return ResponseEntity.ok(emailService.verifyCode(issuedCode, email));
    }

    // 회원가입 로직
    @PostMapping("/signup")
    public ResponseEntity<ResponseMsgDto> emailSignup(@RequestBody @Valid SignupRequestDto dto) {
        emailService.emailSignup(dto);
        return ResponseEntity.ok(new ResponseMsgDto(StatusMsgCode.SIGN_UP));
    }
}

 

EmailEntity

package com.cloneweek.hanghaebnb.util.email;

import lombok.Getter;
import lombok.NoArgsConstructor;
import javax.persistence.*;

@Getter
@Entity
@NoArgsConstructor
public class Code {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @Column(nullable = false)
    private String email;

    @Column(nullable = false)
    private String randomCode;

    public Code(String randomCode, String email) {
        this.randomCode = randomCode;
        this.email = email;
    }
}

 

EmailConfirmDto

package com.cloneweek.hanghaebnb.util.email;

import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;

@Getter
@NoArgsConstructor
@AllArgsConstructor
public class EmailConfirmDto {
    private String email;
}

 

EmailRepository

package com.cloneweek.hanghaebnb.util.email;

import org.springframework.data.jpa.repository.JpaRepository;

import java.util.List;

public interface CodeRepository extends JpaRepository<Code, Long> {
    List<Code> findByEmail(String email);
    Code findByEmailAndId(String email, Long id);
}

 

EmailService

package com.cloneweek.hanghaebnb.util.email;

import com.cloneweek.hanghaebnb.dto.ResponseDto.ResponseBoolDto;
import com.cloneweek.hanghaebnb.dto.ResponseDto.ResponseMsgDto;
import com.cloneweek.hanghaebnb.util.exception.CustomException;
import com.cloneweek.hanghaebnb.dto.RequestDto.SignupRequestDto;
import com.cloneweek.hanghaebnb.entity.User;
import com.cloneweek.hanghaebnb.repository.UserRepository;
import lombok.RequiredArgsConstructor;
import org.springframework.mail.MailException;
import org.springframework.mail.javamail.JavaMailSender;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;

import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeMessage;
import java.util.List;
import java.util.Random;

import static com.cloneweek.hanghaebnb.util.exception.StatusMsgCode.*;

@Service
@RequiredArgsConstructor
public class EmailService {
    private final JavaMailSender emailSender;
    private final PasswordEncoder passwordEncoder;
    private final UserRepository userRepository;
    private final CodeRepository codeRepository;


    // 랜덤하게 만든 인증코드 메일로 발송하는 메서드
    public String sendSimpleMessage(String email) throws Exception {
        if (userRepository.findByEmail(email).isPresent()) {
            throw new CustomException(EXIST_USER);
        }
        String randomCode = "";
        StringBuffer key = new StringBuffer();
        Random rnd = new Random();

        // 인증코드 랜덤하게 만들기. 인증 요청될 때마다 새롭게만들어 메일로 보냄
        for (int i = 0; i < 8; i++) { // 인증코드 8자리
            int index = rnd.nextInt(3); // 0~2 까지 랜덤

            switch (index) {
                case 0:
                    key.append((char) ((int) (rnd.nextInt(26)) + 97));
                    //  a~z  (ex. 1+97=98 => (char)98 = 'b')
                    break;
                case 1:
                    key.append((char) ((int) (rnd.nextInt(26)) + 65));
                    //  A~Z
                    break;
                case 2:
                    key.append((rnd.nextInt(10)));
                    // 0~9
                    break;
            }
        }
        randomCode = key.toString();

        // 메세지 내용 작성
        MimeMessage message = emailSender.createMimeMessage();

        message.addRecipients(MimeMessage.RecipientType.TO, email);
        message.setSubject("hanghaebnb 회원가입 이메일 인증");
        String msgg = "";
        msgg += "<div style='margin:100px;'>";
        msgg += "<h1> 안녕하세요 hanghaebnb입니다. </h1>";
        msgg += "<br>";
        msgg += "<p>아래 코드를 회원가입 창으로 돌아가 입력해주세요<p>";
        msgg += "<br>";
        msgg += "<p>감사합니다!<p>";
        msgg += "<br>";
        msgg += "<div align='center' style='border:1px solid black; font-family:verdana';>";
        msgg += "<h3 style='color:blue;'>회원가입 인증 코드입니다.</h3>";
        msgg += "<div style='font-size:130%'>";
        msgg += "CODE : <strong>";
        msgg += randomCode + "</strong><div><br/> ";
        msgg += "</div>";
        message.setText(msgg, "utf-8", "html");//내용
        message.setFrom(new InternetAddress("hanghaebnb@gmail.com", "test"));//보내는 사람

        // 메세지 발송
        try {
            emailSender.send(message);
        } catch (MailException es) {
            es.printStackTrace();
            throw new IllegalArgumentException();
        }

        // 만들었던 랜덤코드 DB에 저장하기
        Code code = new Code(randomCode, email);
        codeRepository.save(code);
        return randomCode;
    }

    // 회원가입
    public void emailSignup(SignupRequestDto dto) {
        String email = dto.getEmail();
        String password = passwordEncoder.encode(dto.getPassword());
        String nickname = dto.getNickname();

        // DB에 이메일과 닉네임 중복 있는지 체크
        if (userRepository.findByEmail(email).isPresent()) {
            throw new CustomException(EXIST_USER);
        }
        if (userRepository.findByNickname(nickname).isPresent()) {
            throw new CustomException(EXIST_NICK);
        }

        // DB에 회원가입할 데이터 저장. builder 패턴으로 적용해봄.
        // User user = new User(email, password, nickname);
        User user = User.builder().email(email)
                .kakaoNickname(nickname)
                .password(password)
                .build();                              // 생성자 호출

        userRepository.save(user);
    }

    // 이메일로 받은 랜덤코드 대조
    public ResponseMsgDto verifyCode(String issuedCode, String email) {

        // 리파지토리에서 해당 이메일로 저장된 모든 랜덤코드 불러오기
        // 리파지토리에서 불러온 랜덤코드 중 가장 최신순으로 발행된 코드 찾기
        List<Code> codeList = codeRepository.findByEmail(email);
        long id = 0L;
        for (Code code : codeList) {
            if (code.getId() > id) {
                id = code.getId();
            }
        }

        // 해당 email로 보낸 가장 최신 랜덤코드 불러오기
        Code code = codeRepository.findByEmailAndId(email, id);
        Boolean result = code.getRandomCode().equals(issuedCode);
        return new ResponseBoolDto(result? MATCH_CODE : UNMATCH_CODE, result);
    }
}

 

Email Properties

mail.smtp.auth=true
mail.smtp.starttls.required=true
mail.smtp.starttls.enable=true
mail.smtp.socketFactory.class=javax.net.ssl.SSLSocketFactory
mail.smtp.socketFactory.fallback=false
mail.smtp.port=465
mail.smtp.socketFactory.port=465

#admin ?? ??
AdminMail.id= 계정 이메일 주소
AdminMail.password= 계정 이메일 비밀번호

[ 참고 자료 ]

 

https://hyunmin1906.tistory.com/276

 

[Go] Google Gmail SMTP 설정 방법 및 메일 전송

■ SMTP 간이 우편 전송 프로토콜(Simple Mail Transfer Protocol)의 약자. 이메일 전송에 사용되는 네트워크 프로토콜이다. 인터넷에서 메일 전송에 사용되는 표준이다. 1982년 RFC821에서 표준화되어 현재

hyunmin1906.tistory.com

https://javaju.tistory.com/100

https://badstorage.tistory.com/38

 

+ Recent posts