From 0c3e783f69502c5ff251a2e1763aba3d22120df0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B0=95=EC=A7=80=EB=AF=BC?= Date: Tue, 17 Dec 2024 12:15:39 +0900 Subject: [PATCH 1/2] =?UTF-8?q?Feat:=20=EB=B9=84=EB=B0=80=EB=B2=88?= =?UTF-8?q?=ED=98=B8=20=EC=9E=AC=EB=B0=9C=EA=B8=89=20=EA=B8=B0=EB=8A=A5=20?= =?UTF-8?q?=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 이메일 전송을 위한 의존성 추가 - EmailService에 이메일 전송을 위한 로직 구현 - UserService의 resetPassword 메서드에서 임시 비밀번호 생성 Related to: #47 --- .../.idea/modules/refrigerator.main.iml | 6 ++- refrigerator/build.gradle | 2 + .../controller/user/UserController.java | 11 +++++- .../user/request/PasswordResetRequest.java | 8 ++++ .../repository/user/UserRepository.java | 1 + .../service/email/EmailService.java | 5 +++ .../service/email/EmailServiceImpl.java | 38 +++++++++++++++++++ .../service/user/UserService.java | 2 + .../service/user/UserServiceImpl.java | 35 +++++++++++++---- 9 files changed, 98 insertions(+), 10 deletions(-) create mode 100644 refrigerator/src/main/java/moja/refrigerator/dto/user/request/PasswordResetRequest.java create mode 100644 refrigerator/src/main/java/moja/refrigerator/service/email/EmailService.java create mode 100644 refrigerator/src/main/java/moja/refrigerator/service/email/EmailServiceImpl.java diff --git a/refrigerator/.idea/modules/refrigerator.main.iml b/refrigerator/.idea/modules/refrigerator.main.iml index ac8e328..397c268 100644 --- a/refrigerator/.idea/modules/refrigerator.main.iml +++ b/refrigerator/.idea/modules/refrigerator.main.iml @@ -1,6 +1,9 @@ + + + @@ -17,5 +20,4 @@ - - + \ No newline at end of file diff --git a/refrigerator/build.gradle b/refrigerator/build.gradle index bc46bee..156aa6d 100644 --- a/refrigerator/build.gradle +++ b/refrigerator/build.gradle @@ -41,6 +41,8 @@ dependencies { implementation 'io.jsonwebtoken:jjwt-impl:0.12.3' implementation 'io.jsonwebtoken:jjwt-jackson:0.12.3' testImplementation 'org.springframework.boot:spring-boot-starter-test' + + implementation 'org.springframework.boot:spring-boot-starter-mail' } tasks.named('test') { useJUnitPlatform() diff --git a/refrigerator/src/main/java/moja/refrigerator/controller/user/UserController.java b/refrigerator/src/main/java/moja/refrigerator/controller/user/UserController.java index cbd031e..9ac8062 100644 --- a/refrigerator/src/main/java/moja/refrigerator/controller/user/UserController.java +++ b/refrigerator/src/main/java/moja/refrigerator/controller/user/UserController.java @@ -1,9 +1,11 @@ package moja.refrigerator.controller.user; import jakarta.servlet.http.HttpServletRequest; +import moja.refrigerator.dto.user.request.PasswordResetRequest; import moja.refrigerator.dto.user.request.UserCreateRequest; import moja.refrigerator.dto.user.request.UserUpdateRequest; import moja.refrigerator.service.user.UserService; +import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; @@ -25,7 +27,7 @@ public String getMainPage() { @PostMapping("/auth/join") public ResponseEntity join(@RequestBody UserCreateRequest request) { userService.createUser(request); - return ResponseEntity.ok().body("회원 가입이 완료되었습니다."); + return ResponseEntity.status(HttpStatus.CREATED).body("회원 가입이 완료되었습니다."); } // 회원 정보 수정 @@ -34,4 +36,11 @@ public ResponseEntity update(@RequestBody UserUpdateRequest request) { userService.updateUser(request); return ResponseEntity.ok().body("회원 정보가 수정되었습니다."); } + + // 비밀번호 재발급 + @PostMapping("/password/reset") + public ResponseEntity resetPassword(@RequestBody PasswordResetRequest request) { + userService.resetPassword(request); + return ResponseEntity.ok().body("임시 비밀번호가 이메일로 발송되었습니다."); + } } diff --git a/refrigerator/src/main/java/moja/refrigerator/dto/user/request/PasswordResetRequest.java b/refrigerator/src/main/java/moja/refrigerator/dto/user/request/PasswordResetRequest.java new file mode 100644 index 0000000..89b2c75 --- /dev/null +++ b/refrigerator/src/main/java/moja/refrigerator/dto/user/request/PasswordResetRequest.java @@ -0,0 +1,8 @@ +package moja.refrigerator.dto.user.request; + +import lombok.Data; + +@Data +public class PasswordResetRequest { + private String userEmail; +} diff --git a/refrigerator/src/main/java/moja/refrigerator/repository/user/UserRepository.java b/refrigerator/src/main/java/moja/refrigerator/repository/user/UserRepository.java index b3b44c0..1742c4b 100644 --- a/refrigerator/src/main/java/moja/refrigerator/repository/user/UserRepository.java +++ b/refrigerator/src/main/java/moja/refrigerator/repository/user/UserRepository.java @@ -13,4 +13,5 @@ public interface UserRepository extends JpaRepository { boolean existsByUserNickname(String userNickname); Optional findByUserPk(long userPk); Optional findByUserId(String userId); + Optional findByUserEmail(String userEmail); } diff --git a/refrigerator/src/main/java/moja/refrigerator/service/email/EmailService.java b/refrigerator/src/main/java/moja/refrigerator/service/email/EmailService.java new file mode 100644 index 0000000..c189c53 --- /dev/null +++ b/refrigerator/src/main/java/moja/refrigerator/service/email/EmailService.java @@ -0,0 +1,5 @@ +package moja.refrigerator.service.email; + +public interface EmailService { + void sendTempPassword(String name, String email, String tempPassword); +} diff --git a/refrigerator/src/main/java/moja/refrigerator/service/email/EmailServiceImpl.java b/refrigerator/src/main/java/moja/refrigerator/service/email/EmailServiceImpl.java new file mode 100644 index 0000000..ec915b3 --- /dev/null +++ b/refrigerator/src/main/java/moja/refrigerator/service/email/EmailServiceImpl.java @@ -0,0 +1,38 @@ +package moja.refrigerator.service.email; + +import org.springframework.beans.factory.annotation.Value; +import org.springframework.mail.SimpleMailMessage; +import org.springframework.mail.javamail.JavaMailSender; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +@Service +public class EmailServiceImpl implements EmailService { + private final JavaMailSender mailSender; + + public EmailServiceImpl(JavaMailSender mailSender) { + this.mailSender = mailSender; + } + + @Value("${spring.mail.username}") + private String senderEmail; + + @Override + @Transactional + public void sendTempPassword(String name, String email, String tempPassword) { + SimpleMailMessage message = new SimpleMailMessage(); + + message.setTo(email); + message.setFrom(senderEmail); + message.setSubject("[ReciPick] 임시 비밀번호 발급"); + message.setText(String.format(""" + 안녕하세요. %s님! 임시 비밀번호가 발급되었습니다. + + 임시 비밀번호: %s + + 보안을 위해 로그인 후 반드시 비밀번호를 변경해 주세요. + """, name, tempPassword)); + + mailSender.send(message); + } +} diff --git a/refrigerator/src/main/java/moja/refrigerator/service/user/UserService.java b/refrigerator/src/main/java/moja/refrigerator/service/user/UserService.java index 9109d98..ed79a4b 100644 --- a/refrigerator/src/main/java/moja/refrigerator/service/user/UserService.java +++ b/refrigerator/src/main/java/moja/refrigerator/service/user/UserService.java @@ -1,9 +1,11 @@ package moja.refrigerator.service.user; +import moja.refrigerator.dto.user.request.PasswordResetRequest; import moja.refrigerator.dto.user.request.UserCreateRequest; import moja.refrigerator.dto.user.request.UserUpdateRequest; public interface UserService { void createUser(UserCreateRequest request); void updateUser(UserUpdateRequest request); + void resetPassword(PasswordResetRequest request); } diff --git a/refrigerator/src/main/java/moja/refrigerator/service/user/UserServiceImpl.java b/refrigerator/src/main/java/moja/refrigerator/service/user/UserServiceImpl.java index 5309804..3188472 100644 --- a/refrigerator/src/main/java/moja/refrigerator/service/user/UserServiceImpl.java +++ b/refrigerator/src/main/java/moja/refrigerator/service/user/UserServiceImpl.java @@ -1,11 +1,13 @@ package moja.refrigerator.service.user; +import lombok.RequiredArgsConstructor; import moja.refrigerator.aggregate.user.User; +import moja.refrigerator.dto.user.request.PasswordResetRequest; import moja.refrigerator.dto.user.request.UserCreateRequest; import moja.refrigerator.dto.user.request.UserUpdateRequest; import moja.refrigerator.exception.user.DuplicateUserException; -import moja.refrigerator.repository.user.TokenBlacklistRepository; import moja.refrigerator.repository.user.UserRepository; +import moja.refrigerator.service.email.EmailService; import org.modelmapper.ModelMapper; import org.springframework.security.core.Authentication; import org.springframework.security.core.context.SecurityContextHolder; @@ -16,18 +18,15 @@ import java.util.ArrayList; import java.util.List; +import java.util.UUID; @Service +@RequiredArgsConstructor public class UserServiceImpl implements UserService { private final UserRepository userRepository; private final BCryptPasswordEncoder passwordEncoder; private final ModelMapper modelMapper; - - public UserServiceImpl(UserRepository userRepository, BCryptPasswordEncoder passwordEncoder, ModelMapper modelMapper) { - this.userRepository = userRepository; - this.passwordEncoder = passwordEncoder; - this.modelMapper = modelMapper; - } + private final EmailService emailService; @Override @Transactional @@ -74,6 +73,28 @@ public void updateUser(UserUpdateRequest request) { } } + @Override + @Transactional + public void resetPassword(PasswordResetRequest request) { + // 이메일로 사용자 찾기 + User user = userRepository.findByUserEmail(request.getUserEmail()) + .orElseThrow(() -> new UsernameNotFoundException("해당 이메일로 가입된 계정이 없습니다.")); + + // 임시 비밀번호 생성 + String tempPassword = UUID.randomUUID().toString().substring(0, 12); + + try { + // 이메일 발송 + emailService.sendTempPassword(user.getUserNickname(), user.getUserEmail(), tempPassword); + // 임시 비밀번호로 업데이트 + user.setUserPw(passwordEncoder.encode(tempPassword)); + } catch (Exception e) { + System.out.println("Detailed error: " + e.getMessage()); + throw new RuntimeException("이메일 발송에 실패했습니다."); + } + + } + private void checkDuplicateUser(UserCreateRequest request) { List errors = new ArrayList<>(); if (userRepository.existsByUserId(request.getUserId())) { From e2f463056b6d2ea81ffc9ebfd2f9a7aefbecdcde Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B0=95=EC=A7=80=EB=AF=BC?= Date: Tue, 17 Dec 2024 12:53:05 +0900 Subject: [PATCH 2/2] =?UTF-8?q?Feat:=20=EB=B9=84=EB=B0=80=EB=B2=88?= =?UTF-8?q?=ED=98=B8=20=EC=9E=AC=EC=84=A4=EC=A0=95=20=EA=B8=B0=EB=8A=A5=20?= =?UTF-8?q?=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - PasswordUpdateRequest에서 기존 비밀번호와 새 비밀번호를 받음 - UserService의 updatePassword 메서드에서 비밀번호 변경 - 기존 비밀번호 검증 후 새 비밀번호 저장 Resolves: #47 --- .../controller/user/UserController.java | 8 ++++++++ .../user/request/PasswordUpdateRequest.java | 9 +++++++++ .../service/user/UserService.java | 2 ++ .../service/user/UserServiceImpl.java | 19 +++++++++++++++++++ 4 files changed, 38 insertions(+) create mode 100644 refrigerator/src/main/java/moja/refrigerator/dto/user/request/PasswordUpdateRequest.java diff --git a/refrigerator/src/main/java/moja/refrigerator/controller/user/UserController.java b/refrigerator/src/main/java/moja/refrigerator/controller/user/UserController.java index 9ac8062..3dd637c 100644 --- a/refrigerator/src/main/java/moja/refrigerator/controller/user/UserController.java +++ b/refrigerator/src/main/java/moja/refrigerator/controller/user/UserController.java @@ -2,6 +2,7 @@ import jakarta.servlet.http.HttpServletRequest; import moja.refrigerator.dto.user.request.PasswordResetRequest; +import moja.refrigerator.dto.user.request.PasswordUpdateRequest; import moja.refrigerator.dto.user.request.UserCreateRequest; import moja.refrigerator.dto.user.request.UserUpdateRequest; import moja.refrigerator.service.user.UserService; @@ -43,4 +44,11 @@ public ResponseEntity resetPassword(@RequestBody PasswordResetRequest request userService.resetPassword(request); return ResponseEntity.ok().body("임시 비밀번호가 이메일로 발송되었습니다."); } + + // 비밀번호 재설정 + @PutMapping("/password/update") + public ResponseEntity updatePassword(@RequestBody PasswordUpdateRequest request) { + userService.updatePassword(request); + return ResponseEntity.ok().body("비밀번호가 변경되었습니다."); + } } diff --git a/refrigerator/src/main/java/moja/refrigerator/dto/user/request/PasswordUpdateRequest.java b/refrigerator/src/main/java/moja/refrigerator/dto/user/request/PasswordUpdateRequest.java new file mode 100644 index 0000000..3b6762a --- /dev/null +++ b/refrigerator/src/main/java/moja/refrigerator/dto/user/request/PasswordUpdateRequest.java @@ -0,0 +1,9 @@ +package moja.refrigerator.dto.user.request; + +import lombok.Data; + +@Data +public class PasswordUpdateRequest { + private String currentPw; + private String newPw; +} diff --git a/refrigerator/src/main/java/moja/refrigerator/service/user/UserService.java b/refrigerator/src/main/java/moja/refrigerator/service/user/UserService.java index ed79a4b..cfb1abd 100644 --- a/refrigerator/src/main/java/moja/refrigerator/service/user/UserService.java +++ b/refrigerator/src/main/java/moja/refrigerator/service/user/UserService.java @@ -1,6 +1,7 @@ package moja.refrigerator.service.user; import moja.refrigerator.dto.user.request.PasswordResetRequest; +import moja.refrigerator.dto.user.request.PasswordUpdateRequest; import moja.refrigerator.dto.user.request.UserCreateRequest; import moja.refrigerator.dto.user.request.UserUpdateRequest; @@ -8,4 +9,5 @@ public interface UserService { void createUser(UserCreateRequest request); void updateUser(UserUpdateRequest request); void resetPassword(PasswordResetRequest request); + void updatePassword(PasswordUpdateRequest request); } diff --git a/refrigerator/src/main/java/moja/refrigerator/service/user/UserServiceImpl.java b/refrigerator/src/main/java/moja/refrigerator/service/user/UserServiceImpl.java index 3188472..7329781 100644 --- a/refrigerator/src/main/java/moja/refrigerator/service/user/UserServiceImpl.java +++ b/refrigerator/src/main/java/moja/refrigerator/service/user/UserServiceImpl.java @@ -3,6 +3,7 @@ import lombok.RequiredArgsConstructor; import moja.refrigerator.aggregate.user.User; import moja.refrigerator.dto.user.request.PasswordResetRequest; +import moja.refrigerator.dto.user.request.PasswordUpdateRequest; import moja.refrigerator.dto.user.request.UserCreateRequest; import moja.refrigerator.dto.user.request.UserUpdateRequest; import moja.refrigerator.exception.user.DuplicateUserException; @@ -95,6 +96,24 @@ public void resetPassword(PasswordResetRequest request) { } + @Override + @Transactional + public void updatePassword(PasswordUpdateRequest request) { + Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); + String userId = authentication.getName(); + + User user = userRepository.findByUserId(userId) + .orElseThrow(() -> new UsernameNotFoundException("사용자를 찾을 수 없습니다.")); + + // 기존 비밀번호 검증 + if (!passwordEncoder.matches(request.getCurrentPw(), user.getUserPw())) { + throw new IllegalArgumentException("기존 비밀번호가 일치하지 않습니다."); + } + + // 새 비밀번호 암호화 후 저장 + user.setUserPw(passwordEncoder.encode(request.getNewPw())); + } + private void checkDuplicateUser(UserCreateRequest request) { List errors = new ArrayList<>(); if (userRepository.existsByUserId(request.getUserId())) {