Skip to content

Commit

Permalink
Merge pull request #48 from fine-ants/feat/#45-refreshToken
Browse files Browse the repository at this point in the history
[feat] jwt refresh token을 redis에 저장하지 않도록 변경
  • Loading branch information
yhpark95 authored Dec 1, 2023
2 parents f019502 + e60882f commit d4e36cf
Show file tree
Hide file tree
Showing 4 changed files with 15 additions and 86 deletions.
6 changes: 2 additions & 4 deletions src/main/java/codesquad/fineants/domain/jwt/JwtProvider.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@

import java.time.LocalDateTime;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

import org.springframework.stereotype.Component;
Expand All @@ -29,8 +28,7 @@ public JwtProvider(JwtProperties jwtProperties) {
this.jwtProperties = jwtProperties;
}

public Jwt createJwtWithRefreshTokenBasedOnMember(Member member, String refreshToken, LocalDateTime now) {
Map<String, Object> claims = member.createClaims();
public Jwt createJwtWithRefreshToken(Claims claims, String refreshToken, LocalDateTime now) {
Date expireDateAccessToken = jwtProperties.createExpireAccessTokenDate(now);
Date expireDateRefreshToken = getClaims(refreshToken).getExpiration();
return createJwt(claims, expireDateAccessToken, expireDateRefreshToken);
Expand All @@ -45,7 +43,7 @@ public Jwt createJwtBasedOnMember(Member member, LocalDateTime now) {

private Jwt createJwt(Map<String, Object> claims, Date expireDateAccessToken, Date expireDateRefreshToken) {
String accessToken = createToken(claims, expireDateAccessToken);
String refreshToken = createToken(new HashMap<>(), expireDateRefreshToken);
String refreshToken = createToken(claims, expireDateRefreshToken);
return new Jwt(accessToken, refreshToken, expireDateAccessToken, expireDateRefreshToken);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,9 @@
import codesquad.fineants.domain.oauth.client.DecodedIdTokenPayload;
import codesquad.fineants.domain.oauth.client.OauthClient;
import codesquad.fineants.domain.oauth.repository.OauthClientRepository;
import codesquad.fineants.spring.api.errors.errorcode.MemberErrorCode;
import codesquad.fineants.spring.api.errors.errorcode.OauthErrorCode;
import codesquad.fineants.spring.api.errors.exception.BadRequestException;
import codesquad.fineants.spring.api.errors.exception.FineAntsException;
import codesquad.fineants.spring.api.errors.exception.NotFoundResourceException;
import codesquad.fineants.spring.api.member.request.AuthorizationRequest;
import codesquad.fineants.spring.api.member.request.OauthMemberLogoutRequest;
import codesquad.fineants.spring.api.member.request.OauthMemberRefreshRequest;
Expand All @@ -33,6 +31,7 @@
import codesquad.fineants.spring.api.member.response.OauthMemberLoginResponse;
import codesquad.fineants.spring.api.member.response.OauthMemberRefreshResponse;
import codesquad.fineants.spring.api.member.response.OauthUserProfileResponse;
import io.jsonwebtoken.Claims;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;

Expand Down Expand Up @@ -66,7 +65,6 @@ public OauthMemberLoginResponse login(String provider, String code, String redir
.build());
Member saveMember = memberRepository.save(member);
Jwt jwt = jwtProvider.createJwtBasedOnMember(saveMember, now);
redisService.saveRefreshToken(saveMember.createRedisKey(), jwt);
return OauthMemberLoginResponse.of(jwt, saveMember);
}

Expand Down Expand Up @@ -118,51 +116,29 @@ private String generateRandomNickname() {

public void logout(String accessToken, OauthMemberLogoutRequest request) {
String refreshToken = request.getRefreshToken();
deleteRefreshTokenBy(refreshToken);
banAccessToken(accessToken);
banTokens(accessToken, refreshToken);
}

private void deleteRefreshTokenBy(String refreshToken) {
String email;
private void banTokens(String accessToken, String refreshToken) {
long accessTokenExpiration;
long refreshTokenExpiration;
try {
email = redisService.findEmailBy(refreshToken);
log.debug("리프레시 토큰 값에 따른 이메일 조회 결과 : email={}", email);
} catch (FineAntsException e) {
log.error("리프레시 토큰에 따른 이메일 없음 : {}", e.toString());
return;
}

boolean result = redisService.deleteRefreshToken(String.format("RT:%s", email));
log.debug("리프레쉬 토큰 삭제 결과 : {}", result);
}

private void banAccessToken(String accessToken) {
long expiration;
try {
expiration = ((Integer)jwtProvider.getClaims(accessToken).get("exp")).longValue();
accessTokenExpiration = ((Integer)jwtProvider.getClaims(accessToken).get("exp")).longValue();
refreshTokenExpiration = ((Integer)jwtProvider.getClaims(accessToken).get("exp")).longValue();
} catch (FineAntsException e) {
log.error("토큰 에러 : {}", accessToken);
log.error("액세스 토큰 밴 에러 : {}", e.toString());
return;
}
redisService.banAccessToken(accessToken, expiration);

redisService.banToken(accessToken, accessTokenExpiration);
redisService.banToken(refreshToken, refreshTokenExpiration);
}

public OauthMemberRefreshResponse refreshAccessToken(OauthMemberRefreshRequest request, LocalDateTime now) {
String refreshToken = request.getRefreshToken();

jwtProvider.validateToken(refreshToken);
Claims claims = jwtProvider.getClaims(refreshToken);
log.debug("refreshToken is valid token : {}", refreshToken);

String email = redisService.findEmailBy(refreshToken);
log.debug("findEmailByRefreshToken 결과 : email={}", email);
Member member = memberRepository.findMemberByEmail(email)
.orElseThrow(() -> new NotFoundResourceException(MemberErrorCode.NOT_FOUND_MEMBER));
log.debug("findMemberByEmail 결과 : member={}", member);

Jwt jwt = jwtProvider.createJwtWithRefreshTokenBasedOnMember(member, refreshToken, now);

Jwt jwt = jwtProvider.createJwtWithRefreshToken(claims, refreshToken, now);
return OauthMemberRefreshResponse.from(jwt);
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,18 +1,11 @@
package codesquad.fineants.spring.api.member.service;

import java.util.Objects;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import java.util.regex.Pattern;

import org.apache.logging.log4j.util.Strings;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import codesquad.fineants.domain.jwt.Jwt;
import codesquad.fineants.spring.api.errors.errorcode.JwtErrorCode;
import codesquad.fineants.spring.api.errors.errorcode.OauthErrorCode;
import codesquad.fineants.spring.api.errors.exception.BadRequestException;
import codesquad.fineants.spring.api.errors.exception.UnAuthorizationException;
import lombok.RequiredArgsConstructor;

Expand All @@ -21,43 +14,15 @@
public class OauthMemberRedisService {

private static final String LOGOUT = "logout";
private static final String REFRESH_TOKEN_PREFIX = "RT:";
private static final Pattern REFRESH_TOKEN_PATTERN = Pattern.compile("RT:*");

private final RedisTemplate<String, String> redisTemplate;

public String get(String key) {
return redisTemplate.opsForValue().get(key);
}

public String findEmailBy(String refreshToken) {
Set<String> keys = redisTemplate.keys(REFRESH_TOKEN_PATTERN.pattern());
if (keys == null) {
throw new UnAuthorizationException(JwtErrorCode.EMPTY_TOKEN);
}
return keys.stream()
.filter(key -> Objects.equals(redisTemplate.opsForValue().get(key), refreshToken))
.findAny()
.map(email -> email.replace(REFRESH_TOKEN_PREFIX, Strings.EMPTY))
.orElseThrow(() -> new BadRequestException(JwtErrorCode.INVALID_TOKEN));
}

public void saveRefreshToken(String key, Jwt jwt) {
redisTemplate.opsForValue().set(key,
jwt.getRefreshToken(),
jwt.convertExpireDateRefreshTokenTimeWithLong(),
TimeUnit.MILLISECONDS);
}

public boolean deleteRefreshToken(String key) {
if (key == null) {
return false;
}
return Boolean.TRUE.equals(redisTemplate.delete(key));
}

public void banAccessToken(String accessToken, long expiration) {
redisTemplate.opsForValue().set(accessToken, LOGOUT, expiration, TimeUnit.MILLISECONDS);
public void banToken(String token, long expiration) {
redisTemplate.opsForValue().set(token, LOGOUT, expiration, TimeUnit.MILLISECONDS);
}

public void validateAlreadyLogout(String token) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.mockito.Mock;
import org.mockito.MockedStatic;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
Expand All @@ -29,7 +28,6 @@
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;

import codesquad.fineants.domain.jwt.Jwt;
import codesquad.fineants.domain.member.MemberRepository;
import codesquad.fineants.domain.oauth.client.AuthorizationCodeRandomGenerator;
import codesquad.fineants.spring.api.errors.errorcode.OauthErrorCode;
Expand All @@ -55,15 +53,9 @@ public class MemberServiceTest {
@Autowired
private MemberRepository memberRepository;

@MockBean
private OauthMemberRedisService redisService;

@MockBean
private AuthorizationCodeRandomGenerator authorizationCodeRandomGenerator;

@Mock
private JWT mockJwt;

@AfterEach
void tearDown() {
memberRepository.deleteAllInBatch();
Expand Down Expand Up @@ -94,7 +86,6 @@ void login() throws JsonProcessingException {
OauthAccessTokenResponse mockAccessTokenResponse =
objectMapper.readValue(objectMapper.writeValueAsString(responseBody), OauthAccessTokenResponse.class);
given(webClientWrapper.post(anyString(), any(), any(), any())).willReturn(mockAccessTokenResponse);
willDoNothing().given(redisService).saveRefreshToken(anyString(), any(Jwt.class));

MockedStatic<JWT> jwtMockedStatic = mockStatic(JWT.class);
Verification mockVerification = mock(Verification.class);
Expand Down Expand Up @@ -212,7 +203,6 @@ void loginWithNaver() throws JsonProcessingException {
OauthAccessTokenResponse mockAccessTokenResponse =
objectMapper.readValue(objectMapper.writeValueAsString(responseBody), OauthAccessTokenResponse.class);
given(webClientWrapper.post(anyString(), any(), any(), any())).willReturn(mockAccessTokenResponse);
willDoNothing().given(redisService).saveRefreshToken(anyString(), any(Jwt.class));

Map<String, Object> userProfileResponseBody = new HashMap<>();
userProfileResponseBody.put("response",
Expand Down

0 comments on commit d4e36cf

Please sign in to comment.