[Practice] 메일 전송 (2)
2021. 4. 20. 15:09ㆍSpring/Practice
1. 문제
- 비밀번호 초기화를 위해 이메일로 인증코드를 전송하는 API
2. 풀이
- schema.sql
create table USER (
ID BIGINT auto_increment primary key,
EMAIL VARCHAR(255),
NAME VARCHAR(255),
PASSWORD VARCHAR(255),
PHONE VARCHAR(255),
REG_DATE TIMESTAMP,
UPDATE_DATE TIMESTAMP,
STATUS INTEGER,
LOCK_YN BOOLEAN DEFAULT FALSE,
PASSWORD_RESET_YN BOOLEAN DEFAULT FALSE,
PASSWORD_RESET_KEY VARCHAR(255)
);
...
create table MAIL_TEMPLATE
(
ID BIGINT auto_increment primary key,
TEMPLATE_ID VARCHAR(255),
TITLE VARCHAR(255),
CONTENTS VARCHAR(255),
SEND_EMAIL VARCHAR(255),
SEND_USER_NAME VARCHAR(255),
REG_DATE TIMESTAMP
);
- data.sql
INSERT INTO USER(ID, EMAIL, PASSWORD, PHONE, REG_DATE, UPDATE_DATE, NAME, STATUS, LOCK_YN, PASSWORD_RESET_YN) VALUES(1, 'test1@naver.com', '1111', '010-1111-1111', '2021-01-01 01:01:01.000000', null, '테스트1', 1, 0, 0);
INSERT INTO USER(ID, EMAIL, PASSWORD, PHONE, REG_DATE, UPDATE_DATE, NAME, STATUS, LOCK_YN, PASSWORD_RESET_YN) VALUES(2, 'test2@naver.com', '2222', '010-2222-2222', '2021-01-02 02:02:02.000000', null, '테스트2', 1, 0, 0);
INSERT INTO USER(ID, EMAIL, PASSWORD, PHONE, REG_DATE, UPDATE_DATE, NAME, STATUS, LOCK_YN, PASSWORD_RESET_YN) VALUES(3, 'test3@naver.com', '3333', '010-3333-3333', '2021-01-03 03:03:03.000000', null, '테스트3', 2, 0, 0);
...
INSERT INTO MAIL_TEMPLATE(ID, TEMPLATE_ID, TITLE, CONTENTS, SEND_EMAIL, SEND_USER_NAME, REG_DATE) VALUES
(1, 'USER_RESET_PASSWORD', '{USER_NAME}님의 비밀번호 초기화 요청입니다.', '<div><p>{USER_NAME}님 안녕하세요.</p><p>아래 링크를 클릭하여, 비밀번호를 초기화해주세요.</p><p><a href="{SERVER_URL}/reset?key={RESET_PASSWORD_KEY}">초기화</a></p></div>',
'ozofweird@gmail.com', '관리자', '2021-01-01 01:01:01.000000');
- User.java
package com.example.jpa.sample.user.entity;
import com.example.jpa.sample.user.model.UserStatus;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import javax.persistence.*;
import java.time.LocalDateTime;
@AllArgsConstructor
@NoArgsConstructor
@Builder
@Data
@Entity
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column
private String email;
@Column
private String name;
@Column
private String password;
@Column
private String phone;
@Column
private LocalDateTime regDate;
@Column
private LocalDateTime updateDate;
@Column
private UserStatus status;
@Column
private boolean lockYn;
@Column // 사용자가 패스워드 초기화 요청을 했는지
private boolean passwordResetYn;
@Column
private String passwordResetKey;
}
- UserService.java
package com.example.jpa.sample.user.service;
import com.example.jpa.sample.board.model.ServiceResult;
import com.example.jpa.sample.user.entity.User;
import com.example.jpa.sample.user.model.*;
import java.util.List;
public interface UserService {
...
ServiceResult resetPassword(UserPasswordResetInput userPasswordResetInput);
}
- UserServiceImpl.java
package com.example.jpa.sample.user.service;
import com.example.jpa.sample.board.model.ServiceResult;
import com.example.jpa.sample.common.MailComponent;
import com.example.jpa.sample.common.exception.BizException;
import com.example.jpa.sample.mail.entity.MailTemplate;
import com.example.jpa.sample.mail.repository.MailTemplateRepository;
import com.example.jpa.sample.user.entity.User;
import com.example.jpa.sample.user.entity.UserInterest;
import com.example.jpa.sample.user.model.*;
import com.example.jpa.sample.user.repository.UserCustomRepository;
import com.example.jpa.sample.user.repository.UserInterestRepository;
import com.example.jpa.sample.user.repository.UserRepository;
import com.example.jpa.sample.util.PasswordUtils;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Optional;
import java.util.UUID;
@RequiredArgsConstructor
@Service
public class UserServiceImpl implements UserService {
private final UserRepository userRepository;
private final UserCustomRepository userCustomRepository;
private final UserInterestRepository userInterestRepository;
private final MailComponent mailComponent;
private final MailTemplateRepository mailTemplateRepository;
...
@Override
public ServiceResult resetPassword(UserPasswordResetInput userPasswordResetInput) {
Optional<User> optionalUser = userRepository.findByEmailAndName(userPasswordResetInput.getEmail(), userPasswordResetInput.getName());
if(!optionalUser.isPresent()) {
throw new BizException("회원정보가 존재하지 않습니다.");
}
User user = optionalUser.get();
String passwordResetKey = UUID.randomUUID().toString();
user.setPasswordResetYn(true);
user.setPasswordResetKey(passwordResetKey);
userRepository.save(user);
String serverUrl = "http://localhost:8080";
Optional<MailTemplate> optionalMailTemplate = mailTemplateRepository.findByTemplateId("USER_RESET_PASSWORD");
optionalMailTemplate.ifPresent(e -> {
String fromEmail = e.getSendEmail();
String fromName = e.getSendUserName();
String title = e.getTitle().replaceAll("\\{USER_NAME\\}", user.getName());
String contents = e.getContents().replaceAll("\\{USER_NAME\\}", user.getName())
.replaceAll("\\{SERVER_URL\\}", serverUrl)
.replaceAll("\\{RESET_PASSWORD_KEY\\}", passwordResetKey);
mailComponent.send(fromEmail, fromName, user.getEmail(), user.getName(), title, contents);
});
return ServiceResult.success();
}
}
- UserPasswordResetInput.java
package com.example.jpa.sample.user.model;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import javax.validation.constraints.Email;
import javax.validation.constraints.NotBlank;
@AllArgsConstructor
@NoArgsConstructor
@Builder
@Data
public class UserPasswordResetInput {
@Email(message = "이메일 형식이 아닙니다.")
@NotBlank(message = "이메일은 필수 입력 사항입니다.")
private String email;
@NotBlank(message = "이름은 필수 입력 사항입니다.")
private String name;
}
- user_reset_password.html (resource - mail-template)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>{USER_NAME}님의 비밀번호 초기화 요청입니다.</title>
</head>
<body>
<div>
<p>{USER_NAME}님 안녕하세요.</p>
<p>아래 링크를 클릭하여, 비밀번호를 초기화해주세요.</p>
<p>
<a href="{SERVER_URL}/reset?key={RESET_PASSWORD_KEY}">초기화</a>
</p>
</div>
</body>
</html>
- MailTemplate.java
package com.example.jpa.sample.mail.entity;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import javax.persistence.*;
import java.time.LocalDateTime;
@AllArgsConstructor
@NoArgsConstructor
@Builder
@Data
@Entity
public class MailTemplate {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column
private String templateId;
@Column
private String title;
@Column
private String contents;
@Column
private String sendEmail;
@Column
private String sendUserName;
@Column
private LocalDateTime regDate;
}
- MailTemplateRepository.java
package com.example.jpa.sample.mail.repository;
import com.example.jpa.sample.mail.entity.MailTemplate;
import org.springframework.data.jpa.repository.JpaRepository;
import java.util.Optional;
public interface MailTemplateRepository extends JpaRepository<MailTemplate, Long> {
Optional<MailTemplate> findByTemplateId(String templateId);
}
- ApiUserController.java
package com.example.jpa.sample.user.controller;
import com.auth0.jwt.JWT;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.exceptions.JWTVerificationException;
import com.auth0.jwt.exceptions.SignatureVerificationException;
import com.example.jpa.sample.board.entity.Board;
import com.example.jpa.sample.board.entity.BoardComment;
import com.example.jpa.sample.board.model.ServiceResult;
import com.example.jpa.sample.board.service.BoardService;
import com.example.jpa.sample.common.exception.BizException;
import com.example.jpa.sample.common.model.ResponseResult;
import com.example.jpa.sample.notice.entity.Notice;
import com.example.jpa.sample.notice.entity.NoticeLike;
import com.example.jpa.sample.notice.model.NoticeResponse;
import com.example.jpa.sample.notice.model.ResponseError;
import com.example.jpa.sample.notice.repository.NoticeLikeRepository;
import com.example.jpa.sample.notice.repository.NoticeRepository;
import com.example.jpa.sample.user.entity.User;
import com.example.jpa.sample.user.exception.ExistsEmailException;
import com.example.jpa.sample.user.exception.PasswordNotMatchException;
import com.example.jpa.sample.user.exception.UserNotFoundException;
import com.example.jpa.sample.user.model.*;
import com.example.jpa.sample.user.repository.UserPointRepository;
import com.example.jpa.sample.user.repository.UserRepository;
import com.example.jpa.sample.user.service.UserPointService;
import com.example.jpa.sample.user.service.UserService;
import com.example.jpa.sample.util.JwtUtils;
import com.example.jpa.sample.util.PasswordUtils;
import lombok.RequiredArgsConstructor;
import org.springframework.dao.DataIntegrityViolationException;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.validation.Errors;
import org.springframework.validation.FieldError;
import org.springframework.web.bind.annotation.*;
import javax.servlet.http.HttpServletRequest;
import javax.validation.Valid;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.UUID;
@RequiredArgsConstructor
@RestController
public class ApiUserController {
private final UserRepository userRepository;
private final NoticeRepository noticeRepository;
private final NoticeLikeRepository noticeLikeRepository;
private final BoardService boardService;
private final UserPointService userPointService;
private final UserService userService;
...
//------------------------------------------------------------------------------
// 문제 1
@PostMapping("/api/public/user") // public url에 대해서는 인터셉터가 거치지 않음
public ResponseEntity<?> addUser(@RequestBody UserInput userInput) {
ServiceResult result = userService.addUser(userInput);
return ResponseResult.success(result);
}
// 문제 2
@PostMapping("/api/public/user/password/reset")
public ResponseEntity<?> resetPassword(@RequestBody @Valid UserPasswordResetInput userPasswordResetInput, Errors errors) {
if(errors.hasErrors()) {
return ResponseResult.fail("입력값이 정확하지 않습니다.", ResponseError.of(errors.getAllErrors()));
}
ServiceResult result = null;
try {
result = userService.resetPassword(userPasswordResetInput);
} catch (BizException e) {
return ResponseResult.fail(e.getMessage());
}
return ResponseResult.success(result);
}
}
728x90
'Spring > Practice' 카테고리의 다른 글
[Practice] 메일 전송 (4) (0) | 2021.04.20 |
---|---|
[Practice] 메일 전송 (3) (0) | 2021.04.20 |
[Practice] 메일 전송 (1) (0) | 2021.04.20 |
[Practice] Open API 연동 시 API 프로퍼티 활용 (0) | 2021.04.20 |