Skip to content

Commit

Permalink
Merge pull request #72 from IT-Cotato/develop
Browse files Browse the repository at this point in the history
[Release] : 회원 인증 시 이메일 확인 로직 배포
  • Loading branch information
Youthhing authored Jul 29, 2024
2 parents 8b344be + 2350ef3 commit 91b8127
Show file tree
Hide file tree
Showing 9 changed files with 90 additions and 27 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,8 @@ public ResponseEntity<MemberInfoResponse> findMemberInfo(
public ResponseEntity<Void> updatePassword(@RequestHeader("Authorization") String authorizationHeader,
@RequestBody @Valid UpdatePasswordRequest request) {
String accessToken = jwtTokenProvider.getBearer(authorizationHeader);
memberService.updatePassword(accessToken, request.password());
Long memberId = jwtTokenProvider.getMemberId(accessToken);
memberService.updatePassword(memberId, request.password());
return ResponseEntity.noContent().build();
}

Expand Down
4 changes: 3 additions & 1 deletion src/main/java/org/cotato/csquiz/common/error/ErrorCode.java
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,9 @@ public enum ErrorCode {
INVALID_PASSWORD(HttpStatus.BAD_REQUEST, "A-002", "유효하지 않은 패스워드입니다."),
INVALID_PHONE_NUMBER(HttpStatus.BAD_REQUEST, "A-003", "유효하지 않은 전화번호 입니다."),
CODE_NOT_MATCH(HttpStatus.BAD_REQUEST, "A-101", "요청하신 코드가 일치하지 않습니다."),
EMAIL_NOT_FOUND(HttpStatus.NOT_FOUND, "A-201", "해당 이메일이 존재하지 않습니다."), // 이게 소켓에 왜 필요한지, find 그 부분에서 발생해야할 예외일듯
CODE_EXPIRED(HttpStatus.BAD_REQUEST, "A-102", "코드 유효 시간이 만료되었습니다."),
EMAIL_NOT_FOUND(HttpStatus.NOT_FOUND, "A-201", "해당 이메일이 존재하지 않습니다."),
REQUEST_AGAIN(HttpStatus.NOT_FOUND, "A-202", "해당 이메일에 대한 코드가 존재하지 않습니다. 다시 요청 해주세요"),
EMAIL_DUPLICATED(HttpStatus.CONFLICT, "A-301", "존재하는 이메일 입니다."),
PHONE_NUMBER_DUPLICATED(HttpStatus.CONFLICT, "A-302", "존재하는 전화번호입니다."),

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package org.cotato.csquiz.domain.auth.cache;

import java.util.concurrent.TimeUnit;
import lombok.RequiredArgsConstructor;
import org.cotato.csquiz.domain.auth.enums.EmailType;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;

@Component
@RequiredArgsConstructor
public class EmailRedisRepository {

private static final int EXPIRATION_TIME = 15;
private final RedisTemplate<String, String> redisTemplate;

public Boolean saveEmail(EmailType type, final String email){
String key = type.getKeyPrefix() + email;
return redisTemplate.opsForValue().setIfAbsent(
key,
type.getValue(),
EXPIRATION_TIME,
TimeUnit.MINUTES
);
}

public Boolean isEmailPresent(EmailType type, final String email) {
String key = type.getKeyPrefix() + email;
return redisTemplate.hasKey(key);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import java.util.concurrent.TimeUnit;
import lombok.RequiredArgsConstructor;
import org.cotato.csquiz.domain.auth.enums.EmailType;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Repository;

Expand All @@ -13,13 +14,13 @@ public class VerificationCodeRedisRepository {
private static final String KEY_PREFIX = "$email$";
private final RedisTemplate<String, String> redisTemplate;

public String getByEmail(String email) {
String queryKey = KEY_PREFIX + email;
public String getByEmail(EmailType type, String email) {
String queryKey = type.getKeyPrefix() + KEY_PREFIX + email;
return redisTemplate.opsForValue().get(queryKey);
}

public void saveCodeWithEmail(String email, String verificationCode) {
String saveKey = KEY_PREFIX + email;
public void saveCodeWithEmail(EmailType type, String email, String verificationCode) {
String saveKey = type.getKeyPrefix() + KEY_PREFIX + email;
redisTemplate.opsForValue().set(
saveKey,
verificationCode,
Expand Down
16 changes: 16 additions & 0 deletions src/main/java/org/cotato/csquiz/domain/auth/enums/EmailType.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package org.cotato.csquiz.domain.auth.enums;

import lombok.AllArgsConstructor;
import lombok.Getter;

@Getter
@AllArgsConstructor
public enum EmailType {
SIGNUP("$sign-up", "exist", "코드 일치 성공"),
UPDATE_PASSWORD("$update-pwd", "exist", "코드 요청 후 검증 요청을 보내지 않음")
;

private final String keyPrefix;
private final String value;
private final String description;
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,11 @@
import org.cotato.csquiz.common.config.jwt.RefreshToken;
import org.cotato.csquiz.common.config.jwt.RefreshTokenRepository;
import org.cotato.csquiz.common.config.jwt.Token;
import org.cotato.csquiz.common.error.ErrorCode;
import org.cotato.csquiz.common.error.exception.AppException;
import org.cotato.csquiz.domain.auth.enums.EmailType;
import org.cotato.csquiz.domain.auth.constant.EmailConstants;
import org.cotato.csquiz.domain.auth.entity.Member;
import org.cotato.csquiz.common.error.exception.AppException;
import org.cotato.csquiz.common.error.ErrorCode;
import org.cotato.csquiz.domain.auth.repository.MemberRepository;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
Expand Down Expand Up @@ -119,20 +120,20 @@ public void logout(LogoutRequest request, String refreshToken, HttpServletRespon

public void sendSignUpEmail(SendEmailRequest request) {
validateService.emailNotExist(request.email());
emailVerificationService.sendVerificationCodeToEmail(request.email(), EmailConstants.SIGNUP_SUBJECT);
emailVerificationService.sendVerificationCodeToEmail(EmailType.SIGNUP, request.email(), EmailConstants.SIGNUP_SUBJECT);
}

public void verifySingUpCode(String email, String code) {
emailVerificationService.verifyCode(email, code);
emailVerificationService.verifyCode(EmailType.SIGNUP, email, code);
}

public void sendFindPasswordEmail(SendEmailRequest request) {
validateService.emailExist(request.email());
emailVerificationService.sendVerificationCodeToEmail(request.email(), EmailConstants.PASSWORD_SUBJECT);
emailVerificationService.sendVerificationCodeToEmail(EmailType.UPDATE_PASSWORD, request.email(), EmailConstants.PASSWORD_SUBJECT);
}

public FindPasswordResponse verifyPasswordCode(String email, String code) {
emailVerificationService.verifyCode(email, code);
emailVerificationService.verifyCode(EmailType.UPDATE_PASSWORD, email, code);
Member findMember = memberRepository.findByEmail(email)
.orElseThrow(() -> new AppException(ErrorCode.EMAIL_NOT_FOUND));
String role = findMember.getRole().getKey();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@
import lombok.extern.slf4j.Slf4j;
import org.cotato.csquiz.common.error.exception.AppException;
import org.cotato.csquiz.common.error.ErrorCode;
import org.cotato.csquiz.domain.auth.cache.EmailRedisRepository;
import org.cotato.csquiz.domain.auth.enums.EmailType;
import org.cotato.csquiz.domain.auth.utils.EmailFormValidator;
import org.cotato.csquiz.domain.auth.cache.VerificationCodeRedisRepository;
import org.springframework.mail.javamail.JavaMailSender;
Expand All @@ -33,13 +35,17 @@ public class EmailVerificationService {
private final JavaMailSender mailSender;
private final VerificationCodeRedisRepository verificationCodeRedisRepository;
private final EmailFormValidator emailFormValidator;
private final EmailRedisRepository emailRedisRepository;

@Transactional
public void sendVerificationCodeToEmail(String recipient, String subject) {
public void sendVerificationCodeToEmail(EmailType type, String recipient, String subject) {
emailFormValidator.validateEmailForm(recipient);

String verificationCode = getVerificationCode();
log.info("인증 번호 생성 완료");
verificationCodeRedisRepository.saveCodeWithEmail(recipient, verificationCode);

emailRedisRepository.saveEmail(type, recipient);
verificationCodeRedisRepository.saveCodeWithEmail(type, recipient, verificationCode);

sendEmailWithVerificationCode(recipient, verificationCode, subject);
}

Expand Down Expand Up @@ -82,11 +88,19 @@ private InternetAddress getInternetAddress() {
}
}

@Transactional
public void verifyCode(String email, String code) {
String savedVerificationCode = verificationCodeRedisRepository.getByEmail(email);
validateEmailCodeMatching(savedVerificationCode, code);
log.info("[이메일 인증 완료]: 성공한 이메일 == {}", email);
public void verifyCode(EmailType type, String email, String code) {
String savedVerificationCode = verificationCodeRedisRepository.getByEmail(type, email);
if (savedVerificationCode != null) {
validateEmailCodeMatching(savedVerificationCode, code);
log.info("[이메일 인증 완료]: 성공한 이메일 == {}", email);
return;
}

if (emailRedisRepository.isEmailPresent(type, email)) {
throw new AppException(ErrorCode.CODE_EXPIRED);
} else {
throw new AppException(ErrorCode.REQUEST_AGAIN);
}
}

private void validateEmailCodeMatching(String savedVerificationCode, String code) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,9 @@
import org.cotato.csquiz.common.S3.S3Uploader;
import org.cotato.csquiz.common.config.jwt.JwtTokenProvider;
import org.cotato.csquiz.common.entity.S3Info;
import org.cotato.csquiz.common.error.exception.ImageException;
import org.cotato.csquiz.domain.auth.entity.Member;
import org.cotato.csquiz.common.error.exception.AppException;
import org.cotato.csquiz.common.error.ErrorCode;
import org.cotato.csquiz.common.error.exception.AppException;
import org.cotato.csquiz.common.error.exception.ImageException;
import org.cotato.csquiz.domain.auth.entity.Member;
import org.cotato.csquiz.domain.auth.repository.MemberRepository;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
Expand Down Expand Up @@ -53,10 +51,10 @@ public String findBackFourNumber(Member member) {
}

@Transactional
public void updatePassword(String accessToken, String password) {
Long memberId = jwtTokenProvider.getMemberId(accessToken);
public void updatePassword(final Long memberId, final String password) {
Member findMember = memberRepository.findById(memberId)
.orElseThrow(() -> new EntityNotFoundException("해당 회원을 찾을 수 없습니다."));

validateService.checkPasswordPattern(password);
validateIsSameBefore(findMember.getPassword(), password);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,13 +34,13 @@ public void checkDuplicatePhoneNumber(String phone) {
}

public void emailNotExist(String email) {
if (memberRepository.findByEmail(email).isPresent()) {
if (memberRepository.existsByEmail(email)) {
throw new AppException(ErrorCode.EMAIL_DUPLICATED);
}
}

public void emailExist(String email) {
if (memberRepository.findByEmail(email).isEmpty()) {
if (!memberRepository.existsByEmail(email)) {
throw new AppException(ErrorCode.EMAIL_NOT_FOUND);
}
}
Expand Down

0 comments on commit 91b8127

Please sign in to comment.