Skip to content

Commit

Permalink
feat : 토큰 재발급 생성
Browse files Browse the repository at this point in the history
  • Loading branch information
SIWON990327 committed Dec 6, 2024
1 parent 1ce825d commit 008d721
Show file tree
Hide file tree
Showing 11 changed files with 177 additions and 7 deletions.
3 changes: 3 additions & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,9 @@ dependencies {
implementation 'com.fasterxml.jackson.core:jackson-databind:2.16.1'
implementation 'com.fasterxml.jackson.core:jackson-annotations:2.16.1'
implementation 'com.fasterxml.jackson.core:jackson-core:2.16.1'

// redis
implementation 'org.springframework.boot:spring-boot-starter-data-redis'
}

tasks.named('test') {
Expand Down
48 changes: 44 additions & 4 deletions src/main/java/com/groom/orbit/auth/app/AuthService.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
package com.groom.orbit.auth.app;

import java.util.concurrent.TimeUnit;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

Expand All @@ -8,10 +12,11 @@
import com.groom.orbit.auth.app.dto.LoginResponseDto;
import com.groom.orbit.auth.dao.AuthMemberRepository;
import com.groom.orbit.auth.dao.entity.AuthMember;
import com.groom.orbit.config.security.oAuth.AuthTokenGenerator;
import com.groom.orbit.config.security.oAuth.OAuthInfoResponse;
import com.groom.orbit.config.security.oAuth.OAuthLoginParams;
import com.groom.orbit.config.security.oAuth.RequestOAuthInfoService;
import com.groom.orbit.common.exception.CommonException;
import com.groom.orbit.common.exception.ErrorCode;
import com.groom.orbit.config.security.JwtTokenProvider;
import com.groom.orbit.config.security.kakao.KakaoReissueParams;
import com.groom.orbit.config.security.oAuth.*;

import lombok.RequiredArgsConstructor;

Expand All @@ -24,6 +29,11 @@ public class AuthService {
private final AuthTokenGenerator authTokensGenerator;
private final RequestOAuthInfoService requestOAuthInfoService;
private final VectorService vectorService;
private final JwtTokenProvider jwtTokenProvider;
private final RedisTemplate<String, String> redisTemplate;

@Value("${jwt.refresh-token-validity}")
private Long refreshTokenValidityMilliseconds;

public LoginResponseDto login(OAuthLoginParams params) {
OAuthInfoResponse oAuthInfoResponse = requestOAuthInfoService.request(params);
Expand Down Expand Up @@ -55,4 +65,34 @@ private void saveVector(AuthMember member) {
CreateVectorDto vectorDto = CreateVectorDto.builder().memberId(member.getId()).build();
vectorService.save(vectorDto);
}

public AuthToken reissue(KakaoReissueParams params) {

String refreshToken = params.getRefreshToken();

Long memberId = jwtTokenProvider.parseRefreshToken(refreshToken);

if (!refreshToken.equals(redisTemplate.opsForValue().get(memberId.toString()))) {
throw new CommonException(ErrorCode.INVALID_TOKEN_ERROR);
}

AuthMember member =
memberRepository
.findById(memberId)
.orElseThrow(() -> new CommonException(ErrorCode.NOT_FOUND_MEMBER));

String newAccessToken = jwtTokenProvider.generateAccessToken(member.getId());

String newRefreshToken = jwtTokenProvider.generateRefreshToken(member.getId());

redisTemplate
.opsForValue()
.set(
member.getId().toString(),
newRefreshToken,
refreshTokenValidityMilliseconds,
TimeUnit.MILLISECONDS);

return AuthToken.of(newAccessToken, newRefreshToken);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@
import com.groom.orbit.auth.app.dto.LoginResponseDto;
import com.groom.orbit.common.exception.BaseResponse;
import com.groom.orbit.config.security.kakao.KakaoLoginParams;
import com.groom.orbit.config.security.kakao.KakaoReissueParams;
import com.groom.orbit.config.security.oAuth.AuthToken;

import lombok.RequiredArgsConstructor;

Expand All @@ -23,4 +25,9 @@ public class AuthController {
public BaseResponse<LoginResponseDto> loginKakao(@RequestBody KakaoLoginParams params) {
return BaseResponse.onSuccess(authService.login(params));
}

@PostMapping("/reissue")
public BaseResponse<AuthToken> reissue(@RequestBody KakaoReissueParams params) {
return BaseResponse.onSuccess(authService.reissue(params));
}
}
32 changes: 32 additions & 0 deletions src/main/java/com/groom/orbit/config/redis/RedisConfig.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package com.groom.orbit.config.redis;

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.lettuce.LettuceConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.StringRedisSerializer;

@Configuration
public class RedisConfig {
@Value("${spring.data.redis.port}")
private int port;

@Value("${spring.data.redis.host}")
private String host;

@Bean
public RedisConnectionFactory redisConnectionFactory() {
return new LettuceConnectionFactory(host, port);
}

@Bean
public RedisTemplate<String, String> redisTemplate() {
RedisTemplate<String, String> redisTemplate = new RedisTemplate<>();
redisTemplate.setKeySerializer(new StringRedisSerializer());
redisTemplate.setValueSerializer(new StringRedisSerializer());
redisTemplate.setConnectionFactory(redisConnectionFactory());
return redisTemplate;
}
}
12 changes: 12 additions & 0 deletions src/main/java/com/groom/orbit/config/redis/RedisUtil.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package com.groom.orbit.config.redis;

import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Repository;

import lombok.RequiredArgsConstructor;

@RequiredArgsConstructor
@Repository
public class RedisUtil {
private final StringRedisTemplate stringRedisTemplate;
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,15 @@
import java.security.Key;
import java.time.ZonedDateTime;
import java.util.Date;
import java.util.concurrent.TimeUnit;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;

import com.groom.orbit.common.exception.CommonException;
import com.groom.orbit.common.exception.ErrorCode;

import io.jsonwebtoken.Claims;
import io.jsonwebtoken.ExpiredJwtException;
import io.jsonwebtoken.Jws;
Expand All @@ -23,15 +28,18 @@ public class JwtTokenProvider {
private final Key key;
private final long accessTokenValidityMilliseconds;
private final long refreshTokenValidityMilliseconds;
private final RedisTemplate<String, String> redisTemplate;

public JwtTokenProvider(
@Value("${jwt.secret}") String secretKey,
@Value("${jwt.access-token-validity}") final long accessTokenValidityMilliseconds,
@Value("${jwt.refresh-token-validity}") final long refreshTokenValidityMilliseconds) {
@Value("${jwt.refresh-token-validity}") final long refreshTokenValidityMilliseconds,
RedisTemplate<String, String> redisTemplate) {
byte[] keyBytes = Decoders.BASE64.decode(secretKey);
this.key = Keys.hmacShaKeyFor(keyBytes);
this.accessTokenValidityMilliseconds = accessTokenValidityMilliseconds;
this.refreshTokenValidityMilliseconds = refreshTokenValidityMilliseconds;
this.redisTemplate = redisTemplate;
}

public String generate(Long userId, long validityMilliseconds) {
Expand All @@ -55,7 +63,15 @@ public String generateAccessToken(Long userId) {
}

public String generateRefreshToken(Long userId) {
return generate(userId, refreshTokenValidityMilliseconds);
String refreshToken = generate(userId, refreshTokenValidityMilliseconds);
redisTemplate
.opsForValue()
.set(
userId.toString(),
refreshToken,
refreshTokenValidityMilliseconds,
TimeUnit.MILLISECONDS);
return refreshToken;
}

public String extractSubject(String accessToken) {
Expand Down Expand Up @@ -93,4 +109,12 @@ public boolean isTokenValid(String token) {
public Long getSubject(String token) {
return Long.valueOf(getClaims(token).getBody().getSubject());
}

public Long parseRefreshToken(String token) {
if (isTokenValid(token)) {
Claims claims = getClaims(token).getBody();
return Long.parseLong(claims.getSubject());
}
throw new CommonException(ErrorCode.NOT_FOUND_MEMBER);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -81,4 +81,19 @@ public OAuthInfoResponse requestOauthInfo(String accessToken) {

return restTemplate.postForObject(url, request, KakaoInfoResponse.class);
}

@Override
public String reissueAccessToken(KakaoReissueParams params) {
String url = authUrl + "/oauth/token";
HttpHeaders httpHeaders = new HttpHeaders();
httpHeaders.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
MultiValueMap<String, String> body = params.makeBody();
body.add("grant_type", GRANT_TYPE);
body.add("client_id", clientId);
body.add("client_secret", clientSecret);
HttpEntity<?> request = new HttpEntity<>(body, httpHeaders);
KakaoToken response = restTemplate.postForObject(url, request, KakaoToken.class);
assert response != null;
return response.getAccessToken();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package com.groom.orbit.config.security.kakao;

import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;

import lombok.Getter;
import lombok.NoArgsConstructor;

@Getter
@NoArgsConstructor
public class KakaoReissueParams {

private String refreshToken;

public MultiValueMap<String, String> makeBody() {
MultiValueMap<String, String> body = new LinkedMultiValueMap<>();
body.add("refresh_token", refreshToken);
return body;
}
}
Original file line number Diff line number Diff line change
@@ -1,8 +1,12 @@
package com.groom.orbit.config.security.oAuth;

import com.groom.orbit.config.security.kakao.KakaoReissueParams;

public interface OAuthApiClient {

String requestAccessToken(OAuthLoginParams params);

OAuthInfoResponse requestOauthInfo(String accessToken);

String reissueAccessToken(KakaoReissueParams params);
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

import org.springframework.stereotype.Component;

import com.groom.orbit.config.security.kakao.KakaoReissueParams;

@Component
public class RequestOAuthInfoService {

Expand All @@ -15,4 +17,9 @@ public OAuthInfoResponse request(OAuthLoginParams params) {
String accessToken = client.requestAccessToken(params);
return client.requestOauthInfo(accessToken);
}

public OAuthInfoResponse reissue(KakaoReissueParams params) {
String accessToken = client.reissueAccessToken(params);
return client.requestOauthInfo(accessToken);
}
}
8 changes: 7 additions & 1 deletion src/main/resources/application.yml
Original file line number Diff line number Diff line change
Expand Up @@ -54,4 +54,10 @@ cloud:
auto: false
---
fcm:
fcm-url: ${FCM_URL}
fcm-url: ${FCM_URL}
---
spring:
data:
redis:
host: ${REDIS_HOST}
port: ${REDIS_PORT}

0 comments on commit 008d721

Please sign in to comment.