-
Notifications
You must be signed in to change notification settings - Fork 1
[Refactor] redis 적용 #91
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
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -1,38 +1,59 @@ | ||||||||||||||||||||||||||||||||
| package naughty.tuzamate.auth.service; | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| import io.netty.util.internal.StringUtil; | ||||||||||||||||||||||||||||||||
| import jakarta.annotation.PostConstruct; | ||||||||||||||||||||||||||||||||
| import lombok.RequiredArgsConstructor; | ||||||||||||||||||||||||||||||||
| import lombok.extern.slf4j.Slf4j; | ||||||||||||||||||||||||||||||||
| import naughty.tuzamate.auth.entity.RefreshToken; | ||||||||||||||||||||||||||||||||
| import naughty.tuzamate.auth.repository.RefreshTokenRepository; | ||||||||||||||||||||||||||||||||
| import org.springframework.beans.factory.annotation.Autowired; | ||||||||||||||||||||||||||||||||
| import org.springframework.data.redis.core.RedisTemplate; | ||||||||||||||||||||||||||||||||
| import org.springframework.data.redis.core.ValueOperations; | ||||||||||||||||||||||||||||||||
| import org.springframework.stereotype.Service; | ||||||||||||||||||||||||||||||||
| import org.springframework.transaction.annotation.Transactional; | ||||||||||||||||||||||||||||||||
| import org.springframework.util.StringUtils; | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| import javax.swing.text.html.Option; | ||||||||||||||||||||||||||||||||
| import java.time.Duration; | ||||||||||||||||||||||||||||||||
| import java.time.LocalDateTime; | ||||||||||||||||||||||||||||||||
| import java.util.Optional; | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| @Service | ||||||||||||||||||||||||||||||||
| @RequiredArgsConstructor | ||||||||||||||||||||||||||||||||
| @Slf4j | ||||||||||||||||||||||||||||||||
| @Transactional | ||||||||||||||||||||||||||||||||
| //@Transactional | ||||||||||||||||||||||||||||||||
| public class RefreshTokenService { | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| private final RefreshTokenRepository refreshTokenRepository; | ||||||||||||||||||||||||||||||||
| private final RedisTemplate<String, String> redisTemplate; | ||||||||||||||||||||||||||||||||
| private ValueOperations<String, String> valueOperations; | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| @PostConstruct | ||||||||||||||||||||||||||||||||
| private void init() { | ||||||||||||||||||||||||||||||||
| valueOperations = redisTemplate.opsForValue(); | ||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| public void saveRefreshToken(Long userId, String token, LocalDateTime expire) { | ||||||||||||||||||||||||||||||||
| String key = "refreshToken:" + userId; | ||||||||||||||||||||||||||||||||
| Duration ttl = Duration.between(LocalDateTime.now(), expire); | ||||||||||||||||||||||||||||||||
| valueOperations.set(key, token, ttl); | ||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||
|
Comment on lines
35
to
+39
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion TTL calc may be wrong across time zones; add guard for negative/zero TTL expire is LocalDateTime (no zone). If server default zone differs from where expire was computed (e.g., Asia/Seoul), TTL can skew. Also guard negative TTL. - Duration ttl = Duration.between(LocalDateTime.now(), expire);
- valueOperations.set(key, token, ttl);
+ Duration ttl = Duration.between(LocalDateTime.now(), expire);
+ if (!ttl.isNegative() && !ttl.isZero()) {
+ valueOperations.set(key, token, ttl);
+ } else {
+ // Expired or invalid TTL: ensure token isn’t stored
+ redisTemplate.delete(key);
+ }Follow-up (recommended): change the method to accept Instant expireAt and compute Duration.between(Instant.now(), expireAt) to avoid zone issues. 📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| deleteToken(userId); | ||||||||||||||||||||||||||||||||
| public Optional<String> getRefreshToken(Long userId) { | ||||||||||||||||||||||||||||||||
| String key = "refreshToken:" + userId; | ||||||||||||||||||||||||||||||||
| String token = valueOperations.get(key); | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| return StringUtils.hasText(token) ? Optional.of(token) : Optional.empty(); | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| RefreshToken refreshToken = new RefreshToken(token, userId, expire); | ||||||||||||||||||||||||||||||||
| refreshTokenRepository.save(refreshToken); | ||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| public boolean validateToken(String token) { | ||||||||||||||||||||||||||||||||
| public void deleteRefreshToken(Long userId) { | ||||||||||||||||||||||||||||||||
| String key = "refreshToken:" + userId; | ||||||||||||||||||||||||||||||||
| redisTemplate.delete(key); | ||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| /* public boolean validateToken(String token) { | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| Optional<RefreshToken> refreshToken = refreshTokenRepository.findByToken(token); | ||||||||||||||||||||||||||||||||
| return refreshToken.isPresent() && refreshToken.get().getExpireDate().isAfter(LocalDateTime.now()); | ||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| public void deleteToken(Long userId) { | ||||||||||||||||||||||||||||||||
| refreshTokenRepository.deleteByUserId(userId); | ||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||
| }*/ | ||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,48 @@ | ||
| package naughty.tuzamate.global.config; | ||
|
|
||
| import org.springframework.beans.factory.annotation.Value; | ||
| import org.springframework.context.annotation.Bean; | ||
| import org.springframework.context.annotation.Configuration; | ||
| import org.springframework.data.redis.connection.RedisConnectionFactory; | ||
| import org.springframework.data.redis.connection.RedisStandaloneConfiguration; | ||
| import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory; | ||
| import org.springframework.data.redis.core.RedisTemplate; | ||
| import org.springframework.data.redis.core.ValueOperations; | ||
| import org.springframework.data.redis.serializer.StringRedisSerializer; | ||
|
|
||
| @Configuration | ||
| public class RedisConfig { | ||
|
|
||
| @Value("${spring.data.redis.host}") | ||
| private String host; | ||
|
|
||
| @Value("${spring.data.redis.port}") | ||
| private int port; | ||
|
|
||
| @Bean | ||
| public LettuceConnectionFactory redisConnectionFactory() { | ||
| return new LettuceConnectionFactory(new RedisStandaloneConfiguration(host, port)); | ||
| } | ||
|
|
||
| @Bean | ||
| public RedisTemplate<String, String> redisTemplate(RedisConnectionFactory redisConnectionFactory) { | ||
|
|
||
| RedisTemplate<String, String> redisTemplate = new RedisTemplate<>(); | ||
|
|
||
| // redis 연결 | ||
| redisTemplate.setConnectionFactory(redisConnectionFactory); | ||
|
|
||
| // key, value 직렬화 설정 | ||
| StringRedisSerializer serializer = new StringRedisSerializer(); | ||
| redisTemplate.setKeySerializer(serializer); | ||
| redisTemplate.setValueSerializer(serializer); | ||
|
|
||
| // hash key, hash value 직렬화 설정 | ||
| redisTemplate.setHashKeySerializer(serializer); | ||
| redisTemplate.setHashValueSerializer(serializer); | ||
|
|
||
| return redisTemplate; | ||
|
|
||
| } | ||
|
|
||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Bug: maxAge expects seconds (you’re passing millis) → cookie lives 1000× longer
Pass a Duration based on milliseconds.
Apply this diff above where the cookie is built:
📝 Committable suggestion
🤖 Prompt for AI Agents