Skip to content

Commit

Permalink
fix: smsRedisService 타입별 hash 분리
Browse files Browse the repository at this point in the history
  • Loading branch information
psychology50 committed Dec 24, 2023
1 parent f9514d8 commit e2c2d8f
Show file tree
Hide file tree
Showing 16 changed files with 225 additions and 25 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,9 @@
public enum SmsErrorCode implements StatusCode {
EXPIRED_AUTH_CODE(BAD_REQUEST, "인증 시간이 만료되었습니다"),
INVALID_AUTH_CODE(BAD_REQUEST, "유효하지 않은 인증 코드입니다"),
INVALID_RECEIVER(BAD_REQUEST, "유효하지 않은 수신자입니다");
INVALID_RECEIVER(BAD_REQUEST, "유효하지 않은 수신자입니다"),
NOT_FOUND_SMS_PREFIX(BAD_REQUEST, "유효하지 않은 인증 타입입니다.")
;

private final HttpStatus httpStatus;
private final String message;
Expand Down
6 changes: 4 additions & 2 deletions src/main/java/com/kcy/fitapet/domain/oauth/api/OauthApi.java
Original file line number Diff line number Diff line change
Expand Up @@ -93,15 +93,17 @@ private ResponseEntity<?> getResponseEntity(Jwt jwt) {

@PostMapping("/{id}/sms")
public ResponseEntity<?> signUpSmsAuthorization(
@PathVariable("id") Long id,
@RequestParam("provider") ProviderType provider,
@RequestParam(value = "code", required = false) String code,
@RequestBody @Valid SmsReq req
) {
if (code == null) {
SmsRes smsRes = memberAuthService.sendCode(req, SmsPrefix.OAUTH);
SmsRes smsRes = oAuthService.sendCode(req, id, provider, SmsPrefix.OAUTH);
return ResponseEntity.ok(SuccessResponse.from(smsRes));
}

String token = oAuthService.checkCertificationNumber(req, code);
String token = oAuthService.checkCertificationNumber(req, id, code);
if (!StringUtils.hasText(token))
return ResponseEntity.status(HttpStatus.SC_UNAUTHORIZED).build();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,12 +26,16 @@
import com.kcy.fitapet.global.common.security.oauth.dto.OIDCDecodePayload;
import com.kcy.fitapet.global.common.security.oauth.dto.OIDCPublicKeyResponse;
import com.kcy.fitapet.global.common.util.sms.SmsProvider;
import com.kcy.fitapet.global.common.util.sms.dto.SensInfo;
import com.kcy.fitapet.global.common.util.sms.dto.SmsReq;
import com.kcy.fitapet.global.common.util.sms.dto.SmsRes;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDateTime;

@Service
@RequiredArgsConstructor
@Slf4j
Expand Down Expand Up @@ -81,13 +85,23 @@ public Jwt signUpByOIDC(Long id, ProviderType provider, OauthSignUpReq req) {
}

@Transactional
public String checkCertificationNumber(SmsReq req, String code) {
public SmsRes sendCode(SmsReq dto, Long id, ProviderType provider, SmsPrefix prefix) {
SensInfo smsInfo = smsProvider.sendCodeByPhoneNumber(dto);

smsCertificationService.saveSmsAuthToken(dto.to(), smsInfo.code(), prefix);
LocalDateTime expireTime = smsCertificationService.getExpiredTime(dto.to(), prefix);
log.info("인증번호 만료 시간: {}", expireTime);
return SmsRes.of(dto.to(), smsInfo.requestTime(), expireTime);
}

@Transactional
public String checkCertificationNumber(SmsReq req, Long id, String code) {
if (!smsCertificationService.isCorrectCode(req.to(), code, SmsPrefix.REGISTER)) {
log.warn("인증번호 불일치 -> 사용자 입력 인증 번호 : {}", code);
throw new GlobalErrorException(SmsErrorCode.INVALID_AUTH_CODE);
}

String token = jwtUtil.generateSmsOauthToken(SmsAuthInfo.of(1L, req.to()));
String token = jwtUtil.generateSmsOauthToken(SmsAuthInfo.of(id, req.to()));
smsCertificationService.saveSmsAuthToken(req.to(), token, SmsPrefix.OAUTH);

return token;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,10 @@
public interface SmsCertificationService {
/**
* SMS 인증 완료 후 계정 생성을 위한 토큰 저장
* @param phoneNumber : String
* @param key : String
* @param phone : String
* @param code : String
*/
void saveSmsAuthToken(String phoneNumber, String key, SmsPrefix prefix);
void saveSmsAuthToken(String phone, String code, SmsPrefix prefix);

/**
* 인증번호 확인
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package com.kcy.fitapet.global.common.redis.sms;

import com.kcy.fitapet.domain.member.exception.SmsErrorCode;
import com.kcy.fitapet.global.common.redis.sms.dao.*;
import com.kcy.fitapet.global.common.redis.sms.domain.*;
import com.kcy.fitapet.global.common.response.exception.GlobalErrorException;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
Expand All @@ -14,39 +16,82 @@
@Service
@RequiredArgsConstructor
public class SmsCertificationServiceImpl implements SmsCertificationService {
private final SmsCertificationRepository smsCertificationRepository;
private final SmsOauthRepository smsOauthRepository;
private final SmsPasswordRepository smsPasswordRepository;
private final SmsRegisterRepository smsRegisterRepository;
private final SmsUidRepository smsUidRepository;

private final RedisTemplate<String, SmsCertification> redisTemplate;

@Override
public void saveSmsAuthToken(String phoneNumber, String value, SmsPrefix prefix) {
smsCertificationRepository.save(SmsCertification.of(prefix.getTopic(phoneNumber), value));
public void saveSmsAuthToken(String phone, String code, SmsPrefix prefix) {
switch (prefix) {
case OAUTH -> smsOauthRepository.save(SmsOauth.of(phone, code));
case PASSWORD -> smsPasswordRepository.save(SmsPassword.of(phone, code));
case REGISTER -> smsRegisterRepository.save(SmsRegister.of(phone, code));
case UID -> smsUidRepository.save(SmsUid.of(phone, code));
}
}

@Override
public boolean isCorrectCode(String phoneNumber, String code, SmsPrefix prefix) {
Optional<SmsCertification> smsCertification = smsCertificationRepository.findById(prefix.getTopic(phoneNumber));

return smsCertification.map(certification -> certification.getCertificationNumber().equals(code)).orElse(false);
switch (prefix) {
case OAUTH -> {
Optional<SmsOauth> smsOauth = smsOauthRepository.findById(phoneNumber);
return smsOauth.map(oauth -> oauth.getCode().equals(code)).orElse(false);
}
case PASSWORD -> {
Optional<SmsPassword> smsPassword = smsPasswordRepository.findById(phoneNumber);
return smsPassword.map(password -> password.getCode().equals(code)).orElse(false);
}
case REGISTER -> {
Optional<SmsRegister> smsRegister = smsRegisterRepository.findById(phoneNumber);
return smsRegister.map(register -> register.getCode().equals(code)).orElse(false);
}
case UID -> {
Optional<SmsUid> smsUid = smsUidRepository.findById(phoneNumber);
return smsUid.map(uid -> uid.getCode().equals(code)).orElse(false);
}
default -> throw new GlobalErrorException(SmsErrorCode.NOT_FOUND_SMS_PREFIX);
}
}

@Override
public boolean existsCode(String phoneNumber, SmsPrefix prefix) {
return smsCertificationRepository.existsById(prefix.getTopic(phoneNumber));
switch (prefix) {
case OAUTH -> {return smsOauthRepository.existsById(phoneNumber);}
case PASSWORD -> {return smsPasswordRepository.existsById(phoneNumber);}
case REGISTER -> {return smsRegisterRepository.existsById(phoneNumber);}
case UID -> {return smsUidRepository.existsById(phoneNumber);}
default -> throw new GlobalErrorException(SmsErrorCode.NOT_FOUND_SMS_PREFIX);
}
}

@Override
public void removeCode(String phoneNumber, SmsPrefix prefix) {
smsCertificationRepository.deleteById(prefix.getTopic(phoneNumber));
switch (prefix) {
case OAUTH -> {smsOauthRepository.deleteById(phoneNumber);}
case PASSWORD -> {smsPasswordRepository.deleteById(phoneNumber);}
case REGISTER -> {smsRegisterRepository.deleteById(phoneNumber);}
case UID -> {smsUidRepository.deleteById(phoneNumber);}
default -> throw new GlobalErrorException(SmsErrorCode.NOT_FOUND_SMS_PREFIX);
}
}

@Override
public LocalDateTime getExpiredTime(String phoneNumber, SmsPrefix prefix) {
Long ttl = redisTemplate.getExpire("smsCertification:" + prefix.getTopic(phoneNumber));
Long ttl = redisTemplate.getExpire(getKeyName(phoneNumber, prefix));
log.info("ttl: {}", ttl);

if (ttl == null || ttl < 0L)
throw new GlobalErrorException(SmsErrorCode.EXPIRED_AUTH_CODE);

return LocalDateTime.now().plusSeconds(ttl);
}

private String getKeyName(String phoneNumber, SmsPrefix prefix) {
String str = prefix.getPrefix();
return "sms" + str.substring(0, 1).toUpperCase()
+ str.substring(1) + ":" + phoneNumber;
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.kcy.fitapet.global.common.redis.sms;
package com.kcy.fitapet.global.common.redis.sms.dao;

import com.kcy.fitapet.global.common.redis.sms.domain.SmsCertification;
import org.springframework.data.repository.CrudRepository;

public interface SmsCertificationRepository extends CrudRepository<SmsCertification, String> {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package com.kcy.fitapet.global.common.redis.sms.dao;

import com.kcy.fitapet.global.common.redis.sms.domain.SmsOauth;
import org.springframework.data.repository.CrudRepository;

public interface SmsOauthRepository extends CrudRepository<SmsOauth, String> {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package com.kcy.fitapet.global.common.redis.sms.dao;

import com.kcy.fitapet.global.common.redis.sms.domain.SmsPassword;
import org.springframework.data.repository.CrudRepository;

public interface SmsPasswordRepository extends CrudRepository<SmsPassword, String> {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package com.kcy.fitapet.global.common.redis.sms.dao;

import com.kcy.fitapet.global.common.redis.sms.domain.SmsRegister;
import org.springframework.data.repository.CrudRepository;

public interface SmsRegisterRepository extends CrudRepository<SmsRegister, String> {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package com.kcy.fitapet.global.common.redis.sms.dao;

import com.kcy.fitapet.global.common.redis.sms.domain.SmsUid;
import org.springframework.data.repository.CrudRepository;

public interface SmsUidRepository extends CrudRepository<SmsUid, String> {
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package com.kcy.fitapet.global.common.redis.sms;
package com.kcy.fitapet.global.common.redis.sms.domain;

import lombok.Builder;
import lombok.Getter;
Expand All @@ -10,18 +10,18 @@
public class SmsCertification {
@Id
private final String phoneNumber;
private final String certificationNumber;
private final String code;

@Builder
public SmsCertification(String phoneNumber, String certificationNumber) {
public SmsCertification(String phoneNumber, String code) {
this.phoneNumber = phoneNumber;
this.certificationNumber = certificationNumber;
this.code = code;
}

public static SmsCertification of(String phoneNumber, String certificationNumber) {
public static SmsCertification of(String phoneNumber, String code) {
return SmsCertification.builder()
.phoneNumber(phoneNumber)
.certificationNumber(certificationNumber)
.code(code)
.build();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package com.kcy.fitapet.global.common.redis.sms.domain;

import lombok.Builder;
import lombok.Getter;
import org.springframework.data.annotation.Id;
import org.springframework.data.redis.core.RedisHash;

@RedisHash(value = "smsOauth", timeToLive = 300)
@Getter
public class SmsOauth {
@Id
private final String phoneNumber;
private final String code;

@Builder
public SmsOauth(String phoneNumber, String code) {
this.phoneNumber = phoneNumber;
this.code = code;
}

public static SmsOauth of(String phoneNumber, String code) {
return SmsOauth.builder()
.phoneNumber(phoneNumber)
.code(code)
.build();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package com.kcy.fitapet.global.common.redis.sms.domain;

import lombok.Builder;
import lombok.Getter;
import org.springframework.data.annotation.Id;
import org.springframework.data.redis.core.RedisHash;

@RedisHash(value = "smsPassword", timeToLive = 300)
@Getter
public class SmsPassword {
@Id
private final String phoneNumber;
private final String code;

@Builder
public SmsPassword(String phoneNumber, String code) {
this.phoneNumber = phoneNumber;
this.code = code;
}

public static SmsPassword of(String phoneNumber, String code) {
return SmsPassword.builder()
.phoneNumber(phoneNumber)
.code(code)
.build();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package com.kcy.fitapet.global.common.redis.sms.domain;

import lombok.Builder;
import lombok.Getter;
import org.springframework.data.annotation.Id;
import org.springframework.data.redis.core.RedisHash;

@RedisHash(value = "smsRegister", timeToLive = 300)
@Getter
public class SmsRegister {
@Id
private final String phoneNumber;
private final String code;

@Builder
public SmsRegister(String phoneNumber, String code) {
this.phoneNumber = phoneNumber;
this.code = code;
}

public static SmsRegister of(String phoneNumber, String code) {
return SmsRegister.builder()
.phoneNumber(phoneNumber)
.code(code)
.build();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package com.kcy.fitapet.global.common.redis.sms.domain;

import lombok.Builder;
import lombok.Getter;
import org.springframework.data.annotation.Id;
import org.springframework.data.redis.core.RedisHash;

@RedisHash(value = "smsUid", timeToLive = 300)
@Getter
public class SmsUid {
@Id
private final String phoneNumber;
private final String code;

@Builder
public SmsUid(String phoneNumber, String code) {
this.phoneNumber = phoneNumber;
this.code = code;
}

public static SmsUid of(String phoneNumber, String code) {
return SmsUid.builder()
.phoneNumber(phoneNumber)
.code(code)
.build();
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package com.kcy.fitapet.global.config;

import com.kcy.fitapet.global.common.redis.sms.SmsCertification;
import com.kcy.fitapet.global.common.redis.sms.domain.SmsCertification;
import com.kcy.fitapet.global.config.feign.OidcCacheManager;
import com.kcy.fitapet.global.config.feign.RedisCacheConnectionFactory;
import org.springframework.beans.factory.annotation.Value;
Expand Down

0 comments on commit e2c2d8f

Please sign in to comment.