-
Notifications
You must be signed in to change notification settings - Fork 5
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
[BE] 비밀번호 재설정 기능을 구현한다. #843
Merged
Merged
Changes from 38 commits
Commits
Show all changes
39 commits
Select commit
Hold shift + click to select a range
35a8706
feat: 메일 기본 설정 빈 등록
tkdgur0906 8f4c7a1
feat: 메일 보내는 MailSender 구현
tkdgur0906 dea07ab
feat: 비밀번호 찾기 시 검증 이메일 보내는 기능 구현
tkdgur0906 de52e94
feat: 비밀번호 인증 코드 저장로직 추가
tkdgur0906 e9c875c
feat: 비밀번호 인증 코드 발송 기능 엔드포인트 변경
tkdgur0906 8335189
feat: 비밀번호 인증 코드 저장 시 이메일 같이 저장
tkdgur0906 75e149f
feat: PasswordResetCode 스키마 추가
tkdgur0906 583bd8e
feat: 입력받은 비밀번호 초기화 코드 맞는지 확인하는 기능 구현
tkdgur0906 86eb278
feat: 비밀번호 재설정 기능 구현
tkdgur0906 b0f796c
feat: 잘못된 이메일 형식 에러 처리
tkdgur0906 632e1e5
feat: 에러 메시지 명확히 수정
tkdgur0906 fdedae3
test: 비밀번호 초기화 코드 인증 테스트 검증 시간 변경
tkdgur0906 2071fd2
feat: 메일 기본 설정 빈 등록
tkdgur0906 7e9f894
feat: 메일 보내는 MailSender 구현
tkdgur0906 b20e715
feat: 비밀번호 찾기 시 검증 이메일 보내는 기능 구현
tkdgur0906 dd1b87b
feat: 비밀번호 인증 코드 저장로직 추가
tkdgur0906 007507c
feat: 비밀번호 인증 코드 발송 기능 엔드포인트 변경
tkdgur0906 2cb4749
feat: 비밀번호 인증 코드 저장 시 이메일 같이 저장
tkdgur0906 e8b86a9
feat: PasswordResetCode 스키마 추가
tkdgur0906 6813f8c
feat: 입력받은 비밀번호 초기화 코드 맞는지 확인하는 기능 구현
tkdgur0906 bd0688e
feat: 비밀번호 재설정 기능 구현
tkdgur0906 87ebeba
feat: 잘못된 이메일 형식 에러 처리
tkdgur0906 58c9b7a
feat: 에러 메시지 명확히 수정
tkdgur0906 464f311
test: 비밀번호 초기화 코드 인증 테스트 검증 시간 변경
tkdgur0906 b30334f
Merge branch 'dev-be' into feat/807-find-password
tkdgur0906 2afdc80
Merge branch 'feat/807-find-password' of https://github.com/woowacour…
tkdgur0906 f54dfd1
test: 불필요 테스트 코드 삭제
tkdgur0906 eae3528
feat: 인증 코드 유효기간 5분으로 변경
tkdgur0906 a105fb6
feat: 비밀번호 초기화 시 인증 코드 삭제
tkdgur0906 aadbad5
test: 비밀번호 초기화 코드 유효기간 5분으로 변경 반영
tkdgur0906 f7d9fa9
feat: 비밀번호 재설정 시 로그인 타입 Local인 것 반영
tkdgur0906 25dbbbd
test: 테스트 displayname 컨벤션 맞게 수정
tkdgur0906 b42dc5f
refactor: 비밀번호 변경 엔드포인트 수정
tkdgur0906 89c1ac9
style: 불필요 공백 제거
tkdgur0906 1dc75bf
feat: dto 이메일 검증 추가
tkdgur0906 e324e31
refactor: 비밀번호 찾기 기능 서비스 분리
tkdgur0906 27ddcd7
feat: 비밀번호 초기화 코드가 검증 되었는지 여부 확인하도록 추가
tkdgur0906 673e6da
feat: 비밀번호 초기화 코드 생성 시 기존 이메일에 대한 코드 모두 삭제
tkdgur0906 60b521d
feat: 비밀번호 재설정 dto의 인증코드, 새로운 비밀번호 NotBlank 추가
tkdgur0906 File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
56 changes: 56 additions & 0 deletions
56
backend/bang-ggood/src/main/java/com/bang_ggood/auth/domain/PasswordResetCode.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,56 @@ | ||
package com.bang_ggood.auth.domain; | ||
|
||
import com.bang_ggood.BaseEntity; | ||
import com.bang_ggood.user.domain.Email; | ||
import jakarta.persistence.Entity; | ||
import jakarta.persistence.GeneratedValue; | ||
import jakarta.persistence.GenerationType; | ||
import jakarta.persistence.Id; | ||
import lombok.Getter; | ||
import lombok.NoArgsConstructor; | ||
import java.util.Objects; | ||
|
||
import static lombok.AccessLevel.PROTECTED; | ||
|
||
@Getter | ||
@NoArgsConstructor(access = PROTECTED) | ||
@Entity | ||
public class PasswordResetCode extends BaseEntity { | ||
|
||
@Id | ||
@GeneratedValue(strategy = GenerationType.IDENTITY) | ||
private Long id; | ||
|
||
private Email email; | ||
|
||
private String code; | ||
|
||
private boolean verified; | ||
|
||
public PasswordResetCode(String email, String code) { | ||
this.email = new Email(email); | ||
this.code = code; | ||
this.verified = false; | ||
} | ||
|
||
public void verify() { | ||
verified = true; | ||
} | ||
|
||
@Override | ||
public boolean equals(Object o) { | ||
if (this == o) { | ||
return true; | ||
} | ||
if (o == null || getClass() != o.getClass()) { | ||
return false; | ||
} | ||
PasswordResetCode that = (PasswordResetCode) o; | ||
return Objects.equals(id, that.id); | ||
} | ||
|
||
@Override | ||
public int hashCode() { | ||
return Objects.hashCode(id); | ||
} | ||
} |
7 changes: 7 additions & 0 deletions
7
...-ggood/src/main/java/com/bang_ggood/auth/dto/request/ConfirmPasswordResetCodeRequest.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
package com.bang_ggood.auth.dto.request; | ||
|
||
import jakarta.validation.constraints.Email; | ||
|
||
public record ConfirmPasswordResetCodeRequest(@Email(message = "유효하지 않은 이메일 형식입니다.") String email, | ||
String code) { | ||
} |
6 changes: 6 additions & 0 deletions
6
backend/bang-ggood/src/main/java/com/bang_ggood/auth/dto/request/ForgotPasswordRequest.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
package com.bang_ggood.auth.dto.request; | ||
|
||
import jakarta.validation.constraints.Email; | ||
|
||
public record ForgotPasswordRequest(@Email(message = "유효하지 않은 이메일 형식입니다.") String email) { | ||
} |
7 changes: 7 additions & 0 deletions
7
backend/bang-ggood/src/main/java/com/bang_ggood/auth/dto/request/ResetPasswordRequest.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
package com.bang_ggood.auth.dto.request; | ||
|
||
import jakarta.validation.constraints.Email; | ||
|
||
public record ResetPasswordRequest(@Email(message = "유효하지 않은 이메일 형식입니다.") String email, | ||
String code, String newPassword) { | ||
} |
37 changes: 37 additions & 0 deletions
37
.../bang-ggood/src/main/java/com/bang_ggood/auth/repository/PasswordResetCodeRepository.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
package com.bang_ggood.auth.repository; | ||
|
||
import com.bang_ggood.auth.domain.PasswordResetCode; | ||
import com.bang_ggood.global.exception.BangggoodException; | ||
import com.bang_ggood.global.exception.ExceptionCode; | ||
import com.bang_ggood.user.domain.Email; | ||
import org.springframework.data.jpa.repository.JpaRepository; | ||
import org.springframework.data.jpa.repository.Query; | ||
import org.springframework.data.repository.query.Param; | ||
import java.time.LocalDateTime; | ||
import java.util.Optional; | ||
|
||
public interface PasswordResetCodeRepository extends JpaRepository<PasswordResetCode, Long> { | ||
|
||
boolean existsByEmailAndCodeAndVerifiedTrue(Email email, String code); | ||
|
||
@Query("SELECT p FROM PasswordResetCode p " + | ||
"WHERE p.email = :email " + | ||
"AND p.code = :code " + | ||
"AND p.createdAt >= :timeLimit") | ||
Optional<PasswordResetCode> findByEmailAndCodeAndCreatedAtAfter(@Param("email") Email email, | ||
@Param("code") String code, | ||
@Param("timeLimit") LocalDateTime timeLimit); | ||
|
||
default PasswordResetCode getByEmailAndCodeAndCreatedAtAfter(@Param("email") Email email, | ||
@Param("code") String code, | ||
@Param("timeLimit") LocalDateTime timeLimit) { | ||
return findByEmailAndCodeAndCreatedAtAfter(email, code, timeLimit) | ||
.orElseThrow(() -> new BangggoodException(ExceptionCode.AUTHENTICATION_PASSWORD_CODE_NOT_FOUND)); | ||
} | ||
|
||
long countByEmail(Email email); | ||
|
||
void deleteByEmailAndCode(Email email, String code); | ||
|
||
void deleteByEmail(Email email); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
39 changes: 39 additions & 0 deletions
39
backend/bang-ggood/src/main/java/com/bang_ggood/auth/service/MailSender.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
package com.bang_ggood.auth.service; | ||
|
||
import com.bang_ggood.global.exception.BangggoodException; | ||
import com.bang_ggood.global.exception.ExceptionCode; | ||
import jakarta.mail.MessagingException; | ||
import jakarta.mail.internet.MimeMessage; | ||
import lombok.RequiredArgsConstructor; | ||
import org.springframework.mail.javamail.JavaMailSender; | ||
import org.springframework.mail.javamail.MimeMessageHelper; | ||
import org.springframework.stereotype.Component; | ||
|
||
@RequiredArgsConstructor | ||
@Component | ||
public class MailSender { | ||
|
||
private static final String FIND_PASSWORD_MAIL_SUBJECT = "방끗 비밀번호 찾기"; | ||
|
||
private final JavaMailSender javaMailSender; | ||
|
||
public String sendPasswordResetEmail(String email) { | ||
MimeMessage mimeMessage = javaMailSender.createMimeMessage(); | ||
try { | ||
MimeMessageHelper mimeMessageHelper = new MimeMessageHelper(mimeMessage, false, "UTF-8"); | ||
String code = generatePasswordResetCode(); | ||
mimeMessageHelper.setTo(email); | ||
mimeMessageHelper.setSubject(FIND_PASSWORD_MAIL_SUBJECT); | ||
mimeMessageHelper.setText(code); | ||
javaMailSender.send(mimeMessage); | ||
return code; | ||
} catch (MessagingException e) { | ||
throw new BangggoodException(ExceptionCode.MAIL_SEND_ERROR); | ||
} | ||
} | ||
|
||
private String generatePasswordResetCode() { | ||
int code = (int) (Math.random() * 1000000); | ||
return String.format("%06d", code); | ||
} | ||
} |
57 changes: 57 additions & 0 deletions
57
backend/bang-ggood/src/main/java/com/bang_ggood/auth/service/PasswordResetService.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,57 @@ | ||
package com.bang_ggood.auth.service; | ||
|
||
import com.bang_ggood.auth.domain.PasswordResetCode; | ||
import com.bang_ggood.auth.dto.request.ConfirmPasswordResetCodeRequest; | ||
import com.bang_ggood.auth.dto.request.ForgotPasswordRequest; | ||
import com.bang_ggood.auth.dto.request.ResetPasswordRequest; | ||
import com.bang_ggood.auth.repository.PasswordResetCodeRepository; | ||
import com.bang_ggood.global.exception.BangggoodException; | ||
import com.bang_ggood.global.exception.ExceptionCode; | ||
import com.bang_ggood.user.domain.Email; | ||
import com.bang_ggood.user.domain.LoginType; | ||
import com.bang_ggood.user.domain.Password; | ||
import com.bang_ggood.user.repository.UserRepository; | ||
import lombok.RequiredArgsConstructor; | ||
import org.springframework.stereotype.Service; | ||
import org.springframework.transaction.annotation.Transactional; | ||
import java.time.Clock; | ||
import java.time.LocalDateTime; | ||
|
||
@RequiredArgsConstructor | ||
@Service | ||
public class PasswordResetService { | ||
|
||
private static final int PASSWORD_RESET_CODE_EXPIRED_MINUTES = 5; | ||
|
||
private final MailSender mailSender; | ||
private final PasswordResetCodeRepository passwordResetCodeRepository; | ||
private final Clock clock; | ||
private final UserRepository userRepository; | ||
|
||
public void sendPasswordResetEmail(ForgotPasswordRequest request) { | ||
String code = mailSender.sendPasswordResetEmail(request.email()); | ||
passwordResetCodeRepository.deleteByEmail(new Email(request.email())); | ||
passwordResetCodeRepository.save(new PasswordResetCode(request.email(), code)); | ||
} | ||
|
||
@Transactional | ||
public void confirmPasswordResetCode(ConfirmPasswordResetCodeRequest request) { | ||
LocalDateTime timeLimit = LocalDateTime.now(clock).minusMinutes(PASSWORD_RESET_CODE_EXPIRED_MINUTES); | ||
PasswordResetCode passwordResetCode = passwordResetCodeRepository.getByEmailAndCodeAndCreatedAtAfter( | ||
new Email(request.email()), request.code(), timeLimit); | ||
passwordResetCode.verify(); | ||
} | ||
|
||
@Transactional | ||
public void resetPassword(ResetPasswordRequest request) { | ||
Email email = new Email(request.email()); | ||
String code = request.code(); | ||
if (!passwordResetCodeRepository.existsByEmailAndCodeAndVerifiedTrue(email, code)) { | ||
throw new BangggoodException(ExceptionCode.AUTHENTICATION_PASSWORD_CODE_NOT_FOUND); | ||
} | ||
|
||
userRepository.updatePasswordByEmail( | ||
new Email(request.email()), new Password(request.newPassword()), LoginType.LOCAL); | ||
passwordResetCodeRepository.deleteByEmailAndCode(email, code); | ||
} | ||
} |
58 changes: 58 additions & 0 deletions
58
backend/bang-ggood/src/main/java/com/bang_ggood/global/config/MailConfig.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,58 @@ | ||
package com.bang_ggood.global.config; | ||
|
||
import org.springframework.beans.factory.annotation.Value; | ||
import org.springframework.context.annotation.Bean; | ||
import org.springframework.context.annotation.Configuration; | ||
import org.springframework.mail.javamail.JavaMailSender; | ||
import org.springframework.mail.javamail.JavaMailSenderImpl; | ||
import java.util.Properties; | ||
|
||
@Configuration | ||
public class MailConfig { | ||
|
||
private static final String MAIL_TRANSPORT_PROTOCOL = "mail.transport.protocol"; | ||
private static final String MAIL_SMTP_AUTH = "mail.smtp.auth"; | ||
private static final String MAIL_SMTP_STARTTLS_ENABLE = "mail.smtp.starttls.enable"; | ||
private static final String MAIL_DEBUG = "mail.debug"; | ||
|
||
@Value("${spring.mail.host}") | ||
private String host; | ||
|
||
@Value("${spring.mail.username}") | ||
private String username; | ||
|
||
@Value("${spring.mail.password}") | ||
private String password; | ||
|
||
@Value("${spring.mail.port}") | ||
private int port; | ||
|
||
@Value("${spring.mail.properties.mail.transport.protocol}") | ||
private String protocol; | ||
|
||
@Value("${spring.mail.properties.mail.smtp.auth}") | ||
private boolean auth; | ||
|
||
@Value("${spring.mail.properties.mail.starttls.enable}") | ||
private boolean enable; | ||
|
||
@Value("${spring.mail.properties.mail.smtp.debug}") | ||
private boolean debug; | ||
|
||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 공백 👀 |
||
@Bean | ||
public JavaMailSender getJavaMailSender() { | ||
JavaMailSenderImpl mailSender = new JavaMailSenderImpl(); | ||
mailSender.setHost(host); | ||
mailSender.setPort(port); | ||
mailSender.setUsername(username); | ||
mailSender.setPassword(password); | ||
|
||
Properties props = mailSender.getJavaMailProperties(); | ||
props.put(MAIL_TRANSPORT_PROTOCOL, protocol); | ||
props.put(MAIL_SMTP_AUTH, auth); | ||
props.put(MAIL_SMTP_STARTTLS_ENABLE, enable); | ||
props.put(MAIL_DEBUG, debug); | ||
|
||
return mailSender; | ||
} | ||
} |
14 changes: 14 additions & 0 deletions
14
backend/bang-ggood/src/main/java/com/bang_ggood/global/config/TimeConfig.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
package com.bang_ggood.global.config; | ||
|
||
import org.springframework.context.annotation.Bean; | ||
import org.springframework.context.annotation.Configuration; | ||
import java.time.Clock; | ||
|
||
@Configuration | ||
public class TimeConfig { | ||
|
||
@Bean | ||
public Clock clock() { | ||
return Clock.systemDefaultZone(); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
일회성 데이터 같아서 DB에 저장할 필요는 없다고 생각되는데 혹시 다른 방식 고민해보신게 있나요??
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
일단 어딘가에 저장되어야하긴 해서 DB에 저장했습니다.
유효시간도 있다보니 레디스에 저장하는게 최선 같은데, 우선은 간단하게 DB에 저장하는 방식으로 구현했습니다!