Skip to content
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

HotFix : refresh Token 1분 이내에 발급되었을 경우 다시 발급되지 않게 수정 #168

Merged
merged 1 commit into from
Nov 5, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 18 additions & 8 deletions src/main/java/com/example/sinitto/auth/service/TokenService.java
Original file line number Diff line number Diff line change
@@ -1,14 +1,12 @@
package com.example.sinitto.auth.service;

import com.example.sinitto.auth.dto.TokenResponse;
import com.example.sinitto.common.exception.AccessTokenExpiredException;
import com.example.sinitto.common.exception.BadRequestException;
import com.example.sinitto.common.exception.InvalidJwtException;
import com.example.sinitto.common.exception.RefreshTokenStolenException;
import com.example.sinitto.common.exception.*;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.connection.DataType;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

Expand All @@ -26,9 +24,9 @@ public class TokenService {
private static final long REFRESH_SEVEN_DAYS = 1000 * 60 * 60 * 24 * 7;

private final Key secretKey;
private final RedisTemplate<String, String> redisTemplate;
private final RedisTemplate<String, Object> redisTemplate;

public TokenService(@Value("${jwt.secret}") String secretKey, RedisTemplate<String, String> redisTemplate) {
public TokenService(@Value("${jwt.secret}") String secretKey, RedisTemplate<String, Object> redisTemplate) {
byte[] decodedKey = Base64.getDecoder().decode(secretKey);
this.secretKey = new SecretKeySpec(decodedKey, 0, decodedKey.length, "HmacSHA256");
this.redisTemplate = redisTemplate;
Expand All @@ -45,6 +43,10 @@ public String generateAccessToken(String email) {
}

public String generateRefreshToken(String email) {
DataType keyType = redisTemplate.type(email);
if (keyType != null && keyType != DataType.HASH) {
redisTemplate.delete(email);
}
String refreshToken = Jwts.builder()
.setSubject(email)
.claim("tokenType", "refresh")
Expand All @@ -53,7 +55,10 @@ public String generateRefreshToken(String email) {
.signWith(secretKey, SignatureAlgorithm.HS256)
.compact();

redisTemplate.opsForValue().set(email, refreshToken, REFRESH_SEVEN_DAYS, TimeUnit.MILLISECONDS);
redisTemplate.opsForHash().put(email, "refreshToken", refreshToken);
redisTemplate.opsForHash().put(email, "createdAt", System.currentTimeMillis());

redisTemplate.expire(email, REFRESH_SEVEN_DAYS, TimeUnit.MILLISECONDS);
return refreshToken;
}

Expand Down Expand Up @@ -92,7 +97,12 @@ private Claims parseClaims(String token) {
public TokenResponse refreshAccessToken(String refreshToken) {
String email = extractEmailFromRefreshToken(refreshToken);

String storedRefreshToken = redisTemplate.opsForValue().get(email);
String storedRefreshToken = (String) redisTemplate.opsForHash().get(email, "refreshToken");
Long createdAt = Long.parseLong((String) redisTemplate.opsForHash().get(email, "createdAt"));

if (((System.currentTimeMillis() - createdAt) / 1000 / 60) < 1) {
throw new ConflictException("1분 이내에 발급받은 refresh Token이 있습니다.");
}

if (storedRefreshToken == null) {
throw new InvalidJwtException("토큰이 만료되었습니다. 재로그인이 필요합니다.");
Expand Down
30 changes: 30 additions & 0 deletions src/main/java/com/example/sinitto/common/config/RedisConfig.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package com.example.sinitto.common.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.GenericToStringSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;

@Configuration
public class RedisConfig {

@Bean
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory connectionFactory) {
RedisTemplate<String, Object> template = new RedisTemplate<>();
template.setConnectionFactory(connectionFactory);

// Key serializer
template.setKeySerializer(new StringRedisSerializer());
// Value serializer
template.setValueSerializer(new GenericToStringSerializer<>(Object.class));
// Hash key serializer
template.setHashKeySerializer(new StringRedisSerializer());
// Hash value serializer
template.setHashValueSerializer(new GenericToStringSerializer<>(Object.class));

template.afterPropertiesSet();
return template;
}
}
Loading