Skip to content

Commit

Permalink
Merge pull request #37 from Project-Catcher/feat-hg-keygenerator
Browse files Browse the repository at this point in the history
Feat: Key Generator 클래스 적용 &
  • Loading branch information
dev-khg authored Dec 4, 2023
2 parents 39154c4 + c036497 commit 2753517
Show file tree
Hide file tree
Showing 14 changed files with 104 additions and 61 deletions.
6 changes: 3 additions & 3 deletions src/main/java/com/catcher/config/JwtFilter.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@

import com.catcher.common.exception.BaseException;
import com.catcher.core.database.DBManager;
import com.catcher.utils.JwtUtils;
import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
Expand All @@ -17,6 +16,8 @@

import static com.catcher.common.BaseResponseStatus.REDIS_ERROR;
import static com.catcher.utils.HttpServletUtils.getHeader;
import static com.catcher.utils.KeyGenerator.AuthType.BLACK_LIST_ACCESS_TOKEN;
import static com.catcher.utils.KeyGenerator.generateKey;
import static org.apache.http.HttpHeaders.AUTHORIZATION;

/**
Expand Down Expand Up @@ -51,8 +52,7 @@ protected void doFilterInternal(
}

private boolean isBlackList(String accessToken) {
String blackListToken = JwtUtils.generateBlackListToken(accessToken);
return dbManager.getValue(blackListToken).isPresent();
return dbManager.getValue(generateKey(accessToken, BLACK_LIST_ACCESS_TOKEN)).isPresent();
}

private String getAccessToken(HttpServletRequest request) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,10 @@

public interface KeyValueDataStorePort {

void saveValidationCodeWithUserId(String userId, String value);
void saveValidationCodeWithKey(String key, String value);

String findValidationCodeWithKey(String key);

void deleteKey(String key);

}
25 changes: 15 additions & 10 deletions src/main/java/com/catcher/core/service/AuthCodeService.java
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@

import java.util.Random;

import static com.catcher.utils.KeyGenerator.AuthType;
import static com.catcher.utils.KeyGenerator.generateKey;

@Service
@RequiredArgsConstructor
public class AuthCodeService {
Expand All @@ -24,24 +27,26 @@ public int generateSixDigitsRandomCode() {
return random.nextInt(max - min + 1) + min;
}

public String generateAndSaveRandomKey(final String email) {
public String generateAndSaveRandomKey(final String email, final AuthType authType) {
final var user = userRepository.findByEmail(email).orElseThrow(() -> new BaseException(BaseResponseStatus.USERS_NOT_EXISTS));
final var generatedKey = String.valueOf(generateSixDigitsRandomCode());
final var generatedDataStoreKey = generateDataStoreKey(user.getId());
keyValueDataStorePort.saveValidationCodeWithUserId(generatedDataStoreKey, generatedKey);
final var generatedDataStoreKey = generateKey(user.getId(), authType);
keyValueDataStorePort.saveValidationCodeWithKey(generatedDataStoreKey, generatedKey);

return generatedKey;
}

public boolean verifyAuthCode(final String email, String authCode) {
public boolean verifyAuthCode(final String email, String authCode, AuthType authType) {
final var user = userRepository.findByEmail(email).orElseThrow(() -> new BaseException(BaseResponseStatus.USERS_NOT_EXISTS));
final var generatedDataStoreKey = generateDataStoreKey(user.getId());
final var generatedDataStoreKey = generateKey(user.getId(), authType);
final String storedAuthCode = keyValueDataStorePort.findValidationCodeWithKey(generatedDataStoreKey);

return authCode.equals(storedAuthCode);
}
private String generateDataStoreKey(final Long userId) {
return String.format("%s_%s", userId, "AUTHCODE");
}
boolean isSuccess = authCode.equals(storedAuthCode);

if(isSuccess) {
keyValueDataStorePort.deleteKey(generatedDataStoreKey);
}

return isSuccess;
}
}
24 changes: 14 additions & 10 deletions src/main/java/com/catcher/core/service/CaptchaService.java
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@
import java.awt.image.BufferedImage;
import java.util.Objects;

import static com.catcher.utils.KeyGenerator.AuthType;
import static com.catcher.utils.KeyGenerator.generateKey;

@Service
@RequiredArgsConstructor
public class CaptchaService {
Expand All @@ -22,14 +25,13 @@ public class CaptchaService {

private final KeyValueDataStorePort keyValueDataStorePort;

public Captcha generateCaptchaAndSaveAnswer(final String email) {

public Captcha generateCaptchaAndSaveAnswer(final String email, AuthType authType) {
final var user = userRepository.findByEmail(email).orElseThrow(() -> new BaseException(BaseResponseStatus.USERS_NOT_EXISTS));

Captcha captcha = generateCaptcha();

final String generatedUserKey = generateCaptchaUserKey(user.getId());
keyValueDataStorePort.saveValidationCodeWithUserId(generatedUserKey, captcha.getAnswer());
final String generatedUserKey = generateKey(user.getId(), authType);
keyValueDataStorePort.saveValidationCodeWithKey(generatedUserKey, captcha.getAnswer());

return captcha;
}
Expand All @@ -47,17 +49,19 @@ public BufferedImage getImage(Captcha captcha) {
return captcha.getImage();
}

public boolean validateCaptcha(String userEmail, String userAnswer) {
public boolean validateCaptcha(String userEmail, String userAnswer, AuthType authType) {
final var user = userRepository.findByEmail(userEmail).orElseThrow(() -> new BaseException(BaseResponseStatus.USERS_NOT_EXISTS));

final String generatedUserKey = generateCaptchaUserKey(user.getId());
final String generatedUserKey = generateKey(user.getId(), authType);
final String answer = keyValueDataStorePort.findValidationCodeWithKey(generatedUserKey);

return Objects.equals(answer, userAnswer);
}
boolean isSuccess = Objects.equals(answer, userAnswer);

if(isSuccess) {
keyValueDataStorePort.deleteKey(generatedUserKey);
}

private String generateCaptchaUserKey(final Long userId) {
return String.format("%s_%s", userId, "CAPTCHA");
return isSuccess;
}

}
4 changes: 3 additions & 1 deletion src/main/java/com/catcher/core/service/OAuthService.java
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@

import static com.catcher.common.BaseResponseStatus.*;
import static com.catcher.utils.JwtUtils.REFRESH_TOKEN_EXPIRATION_MILLIS;
import static com.catcher.utils.KeyGenerator.AuthType.REFRESH_TOKEN;
import static com.catcher.utils.KeyGenerator.generateKey;

@Service
@Slf4j
Expand Down Expand Up @@ -132,7 +134,7 @@ private TokenDto checkAuthenticationAndGetTokenDto(String username) {
String accessToken = jwtTokenProvider.createAccessToken(authentication);
String refreshToken = jwtTokenProvider.createRefreshToken(authentication);

dbManager.putValue(username, refreshToken, REFRESH_TOKEN_EXPIRATION_MILLIS);
dbManager.putValue(generateKey(username, REFRESH_TOKEN), refreshToken, REFRESH_TOKEN_EXPIRATION_MILLIS);

return new TokenDto(accessToken, refreshToken);
} catch (BadCredentialsException e) {
Expand Down
4 changes: 3 additions & 1 deletion src/main/java/com/catcher/core/service/UserService.java
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
import com.catcher.core.dto.user.UserCreateRequest;
import com.catcher.core.dto.user.UserLoginRequest;
import com.catcher.security.CatcherUser;
import com.catcher.utils.KeyGenerator;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.authentication.AuthenticationManager;
Expand All @@ -28,6 +29,7 @@
import static com.catcher.core.domain.entity.enums.UserProvider.CATCHER;
import static com.catcher.core.domain.entity.enums.UserRole.USER;
import static com.catcher.utils.JwtUtils.REFRESH_TOKEN_EXPIRATION_MILLIS;
import static com.catcher.utils.KeyGenerator.AuthType.*;

@RequiredArgsConstructor
@Transactional(readOnly = true)
Expand Down Expand Up @@ -76,7 +78,7 @@ private TokenDto checkAuthenticationAndGetTokenDto(String username, String passw
String accessToken = jwtTokenProvider.createAccessToken(authentication);
String refreshToken = jwtTokenProvider.createRefreshToken(authentication);

dbManager.putValue(username, refreshToken, REFRESH_TOKEN_EXPIRATION_MILLIS);
dbManager.putValue(KeyGenerator.generateKey(username, REFRESH_TOKEN), refreshToken, REFRESH_TOKEN_EXPIRATION_MILLIS);

return new TokenDto(accessToken, refreshToken);
} catch (BadCredentialsException e) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,18 @@ public class KeyValueDataStoreAdapter implements KeyValueDataStorePort {
private static final long THREE_MINUTES_AS_MILLISECONDS = 180000L;

@Override
public void saveValidationCodeWithUserId(final String userId, final String authCode) {
redisManager.putValue(userId, authCode, THREE_MINUTES_AS_MILLISECONDS);
public void saveValidationCodeWithKey(final String key, final String authCode) {
redisManager.putValue(key, authCode, THREE_MINUTES_AS_MILLISECONDS);
}

@Override
public String findValidationCodeWithKey(final String key) {
return redisManager.getValue(key).orElseThrow(() -> new BaseException(BaseResponseStatus.AUTH_CODE_NOT_FOUND));
}

@Override
public void deleteKey(String key) {
redisManager.deleteKey(key);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,11 @@
import java.util.Optional;

import static com.catcher.common.BaseResponseStatus.NOT_EXIST_REFRESH_JWT;
import static com.catcher.utils.JwtUtils.*;
import static com.catcher.utils.JwtUtils.ACCESS_TOKEN_EXPIRATION_MILLIS;
import static com.catcher.utils.JwtUtils.REFRESH_TOKEN_EXPIRATION_MILLIS;
import static com.catcher.utils.KeyGenerator.AuthType.BLACK_LIST_ACCESS_TOKEN;
import static com.catcher.utils.KeyGenerator.AuthType.REFRESH_TOKEN;
import static com.catcher.utils.KeyGenerator.generateKey;

@Slf4j
@Component
Expand All @@ -28,15 +32,15 @@ public TokenDto reissueRefreshToken(String refreshToken) throws BaseException {

Authentication authentication = jwtTokenProvider.getAuthentication(refreshToken);

String redisRefreshToken = getRefreshToken(authentication.getName());
String redisRefreshToken = getRefreshToken(generateKey(authentication.getName(), REFRESH_TOKEN));

compareRefreshToken(refreshToken, redisRefreshToken);

String newAccessToken = jwtTokenProvider.createAccessToken(authentication);
String newRefreshToken = jwtTokenProvider.createRefreshToken(authentication);

dbManager.deleteKey(refreshToken);
dbManager.putValue(authentication.getName(), newRefreshToken, REFRESH_TOKEN_EXPIRATION_MILLIS);
dbManager.deleteKey(generateKey(refreshToken, REFRESH_TOKEN));
dbManager.putValue(generateKey(authentication.getName(), REFRESH_TOKEN), newRefreshToken, REFRESH_TOKEN_EXPIRATION_MILLIS);

return new TokenDto(newAccessToken, newRefreshToken);
}
Expand All @@ -46,11 +50,11 @@ public void discardRefreshToken(String refreshToken) {
try {
jwtTokenProvider.validateToken(refreshToken);
Authentication authentication = jwtTokenProvider.getAuthentication(refreshToken);
Optional<String> refreshTokenOptional = dbManager.getValue(refreshToken);
Optional<String> refreshTokenOptional = dbManager.getValue(generateKey(refreshToken, REFRESH_TOKEN));
if (refreshTokenOptional.isPresent()) {
compareRefreshToken(refreshToken, refreshTokenOptional.get());
}
dbManager.deleteKey(authentication.getName());
dbManager.deleteKey(generateKey(authentication.getName(), REFRESH_TOKEN));
} catch (BaseException e) {
log.warn("ErrorCode = {}, Message = {}", e.getStatus().getCode(), e.getStatus().getMessage());
}
Expand All @@ -61,8 +65,7 @@ public void discardAccessToken(String accessToken) {
try {
accessToken = getAccessToken(accessToken);
jwtTokenProvider.validateToken(accessToken);
String key = generateBlackListToken(accessToken);
dbManager.putValue(key, "", ACCESS_TOKEN_EXPIRATION_MILLIS);
dbManager.putValue(generateKey(accessToken, BLACK_LIST_ACCESS_TOKEN), "", ACCESS_TOKEN_EXPIRATION_MILLIS);
} catch (BaseException e) {
log.warn("ErrorCode = {}, Message = {}", e.getStatus().getCode(), e.getStatus().getMessage());
}
Expand All @@ -75,8 +78,8 @@ private String getAccessToken(String accessToken) {
return null;
}

private String getRefreshToken(String name) {
return dbManager.getValue(name)
private String getRefreshToken(String key) {
return dbManager.getValue(key)
.orElseThrow(() -> new BaseException(NOT_EXIST_REFRESH_JWT));
}

Expand Down
20 changes: 10 additions & 10 deletions src/main/java/com/catcher/resource/UserController.java
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
import static com.catcher.config.JwtTokenProvider.setRefreshCookie;
import static com.catcher.utils.HttpServletUtils.deleteCookie;
import static com.catcher.utils.JwtUtils.REFRESH_TOKEN_NAME;
import static com.catcher.utils.KeyGenerator.AuthType.FIND_ID;
import static org.springframework.http.HttpHeaders.AUTHORIZATION;

@RequiredArgsConstructor
Expand Down Expand Up @@ -70,29 +71,28 @@ public CommonResponse<Void> logout(HttpServletRequest request,
return success();
}

// TODO: 제목 교체
@Operation(summary = "이메일 인증코드 발송")
@Operation(summary = "ID 찾기 이메일 인증코드 발송")
@PostMapping("/create-authcode/email")
public CommonResponse<Void> sendEmailWithAuthCode(final AuthCodeSendRequest authCodeSendRequest) {

final var key = authCodeService.generateAndSaveRandomKey(authCodeSendRequest.getEmail());
final var key = authCodeService.generateAndSaveRandomKey(authCodeSendRequest.getEmail(), FIND_ID);
emailService.sendEmail(authCodeSendRequest.getEmail(), "title", key);

return success();
}

// TODO: 응답 타입은 따로 생각해보기
@Operation(summary = "인증 코드가 맞는지 검증")
@Operation(summary = "ID 찾기 인증 코드가 맞는지 검증")
@PostMapping("/check-authcode/email")
public CommonResponse<AuthCodeVerifyResponse> verifyAuthCode(final AuthCodeVerifyRequest authCodeVerifyRequest) {
final boolean isVerified = authCodeService.verifyAuthCode(authCodeVerifyRequest.getEmail(), authCodeVerifyRequest.getAuthCode());
final boolean isVerified = authCodeService.verifyAuthCode(authCodeVerifyRequest.getEmail(), authCodeVerifyRequest.getAuthCode(), FIND_ID);

return success(new AuthCodeVerifyResponse(isVerified));
}

@Operation(summary = "캡챠 이미지 생성 및 정답 임시 저장")
@Operation(summary = "ID 찾기 캡챠 이미지 생성 및 정답 임시 저장")
@PostMapping("/captcha/email")
public void captchaGenerate(final CaptchaGenerateRequest captchaGenerateRequest, HttpServletResponse response) throws IOException {
Captcha captcha = captchaService.generateCaptchaAndSaveAnswer(captchaGenerateRequest.getEmail());
Captcha captcha = captchaService.generateCaptchaAndSaveAnswer(captchaGenerateRequest.getEmail(), FIND_ID);

BufferedImage image = captchaService.getImage(captcha);
response.setHeader("Cache-Control", "no-store");
Expand All @@ -101,10 +101,10 @@ public void captchaGenerate(final CaptchaGenerateRequest captchaGenerateRequest,
ImageIO.write(image, "png", response.getOutputStream());
}

@Operation(summary = "캡챠 이미지 정답 검증")
@Operation(summary = "ID 찾기 캡챠 이미지 정답 검증")
@PostMapping("/captcha/validate/email")
public CommonResponse<CaptchaValidateResponse> validateCaptcha(final CaptchaValidateRequest captchaValidateRequest) {
final boolean isValidated = captchaService.validateCaptcha(captchaValidateRequest.getEmail(), captchaValidateRequest.getUserAnswer());
final boolean isValidated = captchaService.validateCaptcha(captchaValidateRequest.getEmail(), captchaValidateRequest.getUserAnswer(), FIND_ID);

return success(new CaptchaValidateResponse(isValidated));

Expand Down
4 changes: 0 additions & 4 deletions src/main/java/com/catcher/utils/JwtUtils.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,5 @@ public class JwtUtils {
public static long REFRESH_TOKEN_EXPIRATION_MILLIS = 1000L * 60 * 60 * 24 * 7; // 7Days

public final static String REFRESH_TOKEN_NAME = "RefreshToken";
public final static String BLACK_LIST_TOKEN = "BlackListToken";

public final static String generateBlackListToken(String accessToken) {
return BLACK_LIST_TOKEN + "[" + accessToken + "]";
}
}
22 changes: 22 additions & 0 deletions src/main/java/com/catcher/utils/KeyGenerator.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package com.catcher.utils;

import lombok.AccessLevel;
import lombok.NoArgsConstructor;

@NoArgsConstructor(access = AccessLevel.PRIVATE)
public final class KeyGenerator {

public static String generateKey(Object obj, AuthType type) {
return String.format("%s:%s", type.name(), obj);
}

public enum AuthType {
BLACK_LIST_ACCESS_TOKEN,
REFRESH_TOKEN,
FIND_ID,
FIND_PASSWORD,
FIND_PASSWORD_SUCCESS,
CAPTCHA_ID,
CAPTCHA_PASSWORD,
}
}
5 changes: 3 additions & 2 deletions src/test/java/com/catcher/core/service/AuthServiceTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import com.catcher.core.dto.TokenDto;
import com.catcher.core.dto.user.UserCreateRequest;
import com.catcher.testconfiguriation.EmbeddedRedisConfiguration;
import com.catcher.utils.KeyGenerator;
import jakarta.persistence.EntityManager;
import jakarta.persistence.PersistenceContext;
import org.junit.jupiter.api.DisplayName;
Expand All @@ -19,7 +20,7 @@
import java.util.Optional;
import java.util.UUID;

import static com.catcher.utils.JwtUtils.generateBlackListToken;
import static com.catcher.utils.KeyGenerator.AuthType.*;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;

Expand Down Expand Up @@ -89,7 +90,7 @@ void discard_access_token() {
//when
authService.discardAccessToken("Bearer " + tokenDto.getAccessToken());
//then
Optional<String> value = dbManager.getValue(generateBlackListToken(tokenDto.getAccessToken()));
Optional<String> value = dbManager.getValue(KeyGenerator.generateKey(tokenDto.getAccessToken(), BLACK_LIST_ACCESS_TOKEN));
assertThat(value).isPresent();
}

Expand Down
Loading

0 comments on commit 2753517

Please sign in to comment.