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

[fix] 로그아웃 API 수정 및 토큰 재발급 API 구현 #66

Merged
merged 8 commits into from
Jan 15, 2024
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package org.sopt.sweet.domain.member.controller;

import com.fasterxml.jackson.core.JsonProcessingException;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.media.Content;
Expand All @@ -11,6 +12,7 @@
import org.sopt.sweet.global.config.auth.UserId;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestParam;

@Tag(name = "소셜로그인", description = "소셜로그인 관련 API")
Expand Down Expand Up @@ -44,14 +46,30 @@ ResponseEntity<SuccessResponse<?>> kakaoLogin(
@ApiResponse(responseCode = "404", content = @Content)
}
)
@Operation(summary = "카카오 로그아웃")
@Operation(summary = "로그아웃")
@PostMapping("/api/oauth/kakao/logout")
ResponseEntity<SuccessResponse<?>> kakaoLogout(
ResponseEntity<SuccessResponse<?>> logout(
@Parameter(
description = "authorization token에서 얻은 userId, 임의입력하면 대체됩니다.",
required = true,
example = "12345"
) @UserId Long userId
);

@ApiResponses(
value = {
@ApiResponse(responseCode = "200"),
@ApiResponse(responseCode = "404", content = @Content)
}
)
@Operation(summary = "토큰 재발급")
@PostMapping("/api/oauth/reissue")
ResponseEntity<SuccessResponse<?>> reissueToken(
@Parameter(
description = "만료된 access token",
required = true,
example = "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9"
) @RequestBody String accessToken
) throws JsonProcessingException;

}
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
package org.sopt.sweet.domain.member.controller;

import com.fasterxml.jackson.core.JsonProcessingException;
import lombok.RequiredArgsConstructor;
import org.sopt.sweet.domain.member.dto.response.KakaoUserInfoResponseDto;
import org.sopt.sweet.domain.member.dto.response.MemberTokenResponseDto;
import org.sopt.sweet.domain.member.dto.response.MemberReissueTokenResponseDto;
import org.sopt.sweet.domain.member.service.OAuthService;
import org.sopt.sweet.global.common.SuccessResponse;
import org.sopt.sweet.global.config.auth.UserId;
Expand Down Expand Up @@ -31,10 +33,17 @@ public ResponseEntity<SuccessResponse<?>> kakaoLogin(@RequestParam("code") Strin
return SuccessResponse.ok(loginResponse);
}

@PostMapping("/kakao/logout")
public ResponseEntity<SuccessResponse<?>> kakaoLogout(@UserId Long userId) {
oAuthService.kakaoLogout(userId);

@PostMapping("/logout")
public ResponseEntity<SuccessResponse<?>> logout(@UserId Long userId) {
oAuthService.logout(userId);
return SuccessResponse.ok("로그아웃 성공");
}

@PostMapping("/reissue")
public ResponseEntity<SuccessResponse<?>> reissueToken(@RequestBody String accessToken) throws JsonProcessingException {
MemberReissueTokenResponseDto memberToken = oAuthService.reissue(accessToken);
return SuccessResponse.ok(memberToken);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package org.sopt.sweet.domain.member.dto.response;

import lombok.Builder;

@Builder
public record MemberReissueTokenResponseDto(
String accessToken
) {
public static MemberReissueTokenResponseDto of(String newAccessToken) {
return MemberReissueTokenResponseDto.builder()
.accessToken(newAccessToken)
.build();
}
}
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
package org.sopt.sweet.domain.member.service;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.gson.JsonElement;
import com.google.gson.JsonParser;
import lombok.RequiredArgsConstructor;
import org.sopt.sweet.domain.member.constant.SocialType;
import org.sopt.sweet.domain.member.dto.response.KakaoUserInfoResponseDto;
import org.sopt.sweet.domain.member.dto.response.MemberReissueTokenResponseDto;
import org.sopt.sweet.domain.member.dto.response.MemberTokenResponseDto;
import org.sopt.sweet.domain.member.entity.Member;
import org.sopt.sweet.domain.member.entity.OAuthToken;
Expand Down Expand Up @@ -143,13 +145,26 @@ public MemberTokenResponseDto saveToken(Long memberId) {
return new MemberTokenResponseDto(accessToken, refreshToken);
}

public void kakaoLogout(Long memberId) {

public void logout(Long memberId) {
String redisKey = "RT:" + memberId;
redisTemplate.delete(redisKey);
}

System.out.println("카카오 로그아웃 성공 :" + memberId);
public MemberReissueTokenResponseDto reissue(String accessToken) throws JsonProcessingException {
Long memberId = Long.valueOf(jwtProvider.decodeJwtPayloadSubject(accessToken));

String redisKey = "RT:" + memberId;
String storedRefreshToken = redisTemplate.opsForValue().get(redisKey);
jwtProvider.validateRefreshToken(storedRefreshToken);

String newAccessToken = issueNewAccessToken(memberId);
String newRefreshToken = issueNewRefreshToken(memberId);
redisTemplate.opsForValue().set(redisKey, newRefreshToken, REFRESH_TOKEN_EXPIRE_TIME, TimeUnit.SECONDS);
return MemberReissueTokenResponseDto.of(newAccessToken);
}


}


Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,9 +40,9 @@ public class SecurityConfig {
"api/v1/swagger-ui/**",
"api/member/token/**",
"api/oauth/kakao/login/**",
"api/oauth/kakao/logout/**",
"api/opengraph",
"api/room/invitations/**"
"api/room/invitations/**",
"api/oauth/reissue/**"
};

@Bean
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package org.sopt.sweet.global.config.auth.jwt;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import io.jsonwebtoken.*;
import io.jsonwebtoken.security.Keys;
import lombok.Getter;
Expand All @@ -8,9 +10,11 @@
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import java.nio.charset.StandardCharsets;
import java.security.Key;
import java.util.Base64;
import java.util.Date;
import java.util.Map;

@Getter
@Component
Expand All @@ -22,6 +26,8 @@ public class JwtProvider {
@Value("${jwt.refresh-token-expire-time}")
private long REFRESH_TOKEN_EXPIRE_TIME;

private final ObjectMapper objectMapper = new ObjectMapper();

public String getIssueToken(Long userId, boolean isAccessToken) {
if (isAccessToken) return generateToken(userId, ACCESS_TOKEN_EXPIRE_TIME);
else return generateToken(userId, REFRESH_TOKEN_EXPIRE_TIME);
Expand Down Expand Up @@ -81,4 +87,15 @@ private Key getSigningKey() {
String encoded = Base64.getEncoder().encodeToString(secretKey.getBytes());
return Keys.hmacShaKeyFor(encoded.getBytes());
}


public String decodeJwtPayloadSubject(String oldAccessToken) throws JsonProcessingException {
return objectMapper.readValue(
new String(Base64.getDecoder().decode(oldAccessToken.split("\\.")[1]), StandardCharsets.UTF_8),
Map.class
).get("sub").toString();
}



}