diff --git a/src/main/java/Journey/Together/domain/member/service/AuthService.java b/src/main/java/Journey/Together/domain/member/service/AuthService.java index 87d235d..edc28b6 100644 --- a/src/main/java/Journey/Together/domain/member/service/AuthService.java +++ b/src/main/java/Journey/Together/domain/member/service/AuthService.java @@ -16,6 +16,7 @@ import Journey.Together.global.security.kakao.dto.KakaoProfile; import Journey.Together.global.security.jwt.TokenProvider; import Journey.Together.global.security.jwt.dto.TokenDto; +import Journey.Together.global.security.kakao.dto.KakaoToken; import Journey.Together.global.security.naver.dto.NaverDeleteResponse; import Journey.Together.global.security.naver.dto.NaverProperties; import Journey.Together.global.security.naver.dto.NaverTokenResponse; @@ -89,7 +90,7 @@ public LoginRes signIn(String token, String type, LoginReq loginReq) throws IOEx interestRepository.save(interest); } tokenDto = tokenProvider.createToken(member); - member.setRefreshToken(tokenDto.refreshToken()); + member.setRefreshToken(loginReq.refreshToken()); // Response return LoginRes.of(member, tokenDto); @@ -156,6 +157,16 @@ public void withdrawal(Member member) { if(naverDeleteResponse.getError() != null){ throw new ApplicationException(ErrorCode.NAVER_DELETE_ERROR); } + }else if(member.getLoginType().equals(LoginType.KAKAO)) { + //accessToken 요청 + KakaoToken kakaoToken = kakaoClient.getKakaoAccessToken(member.getRefreshToken()); + //연결 삭제 + Long id = kakaoClient.unlinkUser(kakaoToken.access_token()); + if(id==null){ + throw new ApplicationException(ErrorCode.KAKAO_REFRESH_TOKEN_ERROR); + }else if(!id.equals(member.getMemberId())){ + throw new ApplicationException(ErrorCode.KAKAO_DELETE_ERROR); + } } memberRepository.delete(member); diff --git a/src/main/java/Journey/Together/global/exception/ErrorCode.java b/src/main/java/Journey/Together/global/exception/ErrorCode.java index e211fab..5f043ed 100644 --- a/src/main/java/Journey/Together/global/exception/ErrorCode.java +++ b/src/main/java/Journey/Together/global/exception/ErrorCode.java @@ -29,7 +29,8 @@ public enum ErrorCode { LOGOUT_TOKEN_EXCEPTION(HttpStatus.UNAUTHORIZED, 3003, "로그아웃된 토큰입니다"), WRONG_TOKEN(HttpStatus.UNAUTHORIZED, 3004, "유효하지 않은 토큰입니다."), WRONG_ACCESS_EXCEPTION(HttpStatus.BAD_REQUEST, 3005, "관리자만 접근 가능합니다."), - + KAKAO_REFRESH_TOKEN_ERROR(HttpStatus.INTERNAL_SERVER_ERROR, 3006, "카카오 토큰에 오류가 있습니다"), + KAKAO_DELETE_ERROR(HttpStatus.INTERNAL_SERVER_ERROR, 3007, "카카오 연결 끊기에 실패하였습니다"), //4000: Apply Error NOT_APPLY_EXCEPTION(HttpStatus.BAD_REQUEST,4000,"지원 기간 지났습니다"), diff --git a/src/main/java/Journey/Together/global/security/kakao/KakaoClient.java b/src/main/java/Journey/Together/global/security/kakao/KakaoClient.java index 5ce05eb..9a6abd5 100644 --- a/src/main/java/Journey/Together/global/security/kakao/KakaoClient.java +++ b/src/main/java/Journey/Together/global/security/kakao/KakaoClient.java @@ -1,6 +1,8 @@ package Journey.Together.global.security.kakao; import Journey.Together.global.security.kakao.dto.KakaoProfile; +import Journey.Together.global.security.kakao.dto.KakaoToken; +import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; @@ -21,9 +23,6 @@ public class KakaoClient { @Value("${spring.security.oauth2.client.registration.kakao.client-secret}") private String kakaoClientSecret; - @Value("${spring.security.oauth2.client.registration.kakao.authorization-grant-type}") - private String kakwaoGrantType; - @Value("${spring.security.oauth2.client.registration.kakao.redirect-uri}") private String kakaoRedirectUri; @@ -33,6 +32,50 @@ public class KakaoClient { @Value("${spring.security.oauth2.client.provider.kakao.user-info-uri}") private String kakaoUserInfoUri; + @Value("${spring.security.oauth2.client.admin-key}") + private String adminKey; + + @Value("${spring.security.oauth2.client.withdrawal.unlink-url}") + private String unlinkUri; + + + /** + * 카카오 서버에 인가코드 기반으로 사용자의 토큰 정보를 조회하는 메소드 + * @param refresh_token - 카카오에서 발급해준 refreshToken 코드 + * @return - 카카오에서 반환한 응답 토큰 객체 + */ + public KakaoToken getKakaoAccessToken(String refresh_token) { + // 요청 보낼 객체 기본 생성 + WebClient webClient = WebClient.create(kakaoTokenUri); + + //요청 본문 + MultiValueMap params = new LinkedMultiValueMap<>(); + params.add("grant_type", "refresh_token"); + params.add("client_id", kakaoClientId); + params.add("refresh_token", refresh_token); + params.add("client_secret", kakaoClientSecret); + + // 요청 보내기 및 응답 수신 + String response = webClient.post() + .uri(kakaoTokenUri) + .header("Content-type", "application/x-www-form-urlencoded") + .body(BodyInserters.fromFormData(params)) + .retrieve() // 데이터 받는 방식, 스프링에서는 exchange는 메모리 누수 가능성 때문에 retrieve 권장 + .bodyToMono(String.class) // (Mono는 단일 데이터, Flux는 복수 데이터) + .block();// 비동기 방식의 데이터 수신 + + // 수신된 응답 Mapping + ObjectMapper objectMapper = new ObjectMapper(); + KakaoToken kakaoToken; + try { + kakaoToken = objectMapper.readValue(response, KakaoToken.class); + } catch (Exception e) { + throw new RuntimeException(e); + } + + return kakaoToken; + } + public KakaoProfile getMemberInfo(String accesToken) { // 요청 기본 객체 생성 WebClient webClient = WebClient.create(kakaoUserInfoUri); @@ -56,4 +99,29 @@ public KakaoProfile getMemberInfo(String accesToken) { return kakaoProfile; } + + //카카오와 연결 끊기 + public Long unlinkUser(String accessToken){ + // 요청 기본 객체 생성 + WebClient webClient = WebClient.create(unlinkUri); + // 요청 보내서 응답 받기 + String response = webClient.post() + .uri(unlinkUri) + .header("Content-Type", "application/x-www-form-urlencoded;charset=utf-8") + .header("Authorization", "Bearer " + accessToken) + .retrieve() + .bodyToMono(String.class) + .block(); + // 수신된 응답 Mapping + ObjectMapper objectMapper = new ObjectMapper(); + try { + JsonNode jsonNode = objectMapper.readTree(response); + if (jsonNode.has("id")) { + return jsonNode.get("id").asLong(); + } + } catch (Exception e) { + throw new RuntimeException(e); + } + return null; + } } diff --git a/src/main/java/Journey/Together/global/security/kakao/dto/KakaoToken.java b/src/main/java/Journey/Together/global/security/kakao/dto/KakaoToken.java new file mode 100644 index 0000000..eaa1eb2 --- /dev/null +++ b/src/main/java/Journey/Together/global/security/kakao/dto/KakaoToken.java @@ -0,0 +1,14 @@ +package Journey.Together.global.security.kakao.dto; + +import lombok.Builder; + +@Builder +public record KakaoToken( + String access_token, + String refresh_token, + String token_type, + Integer expires_in, + Integer refresh_token_expires_in, + String scope +) { +}