Skip to content

Commit

Permalink
Merge pull request #109 from Journey-Together/feature/#14
Browse files Browse the repository at this point in the history
feat : kakao 회원탈퇴 기능
  • Loading branch information
sycuuui authored Sep 22, 2024
2 parents 30bd0e9 + 47972cc commit 95f5faa
Show file tree
Hide file tree
Showing 4 changed files with 99 additions and 5 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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,"지원 기간 지났습니다"),
Expand Down
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -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;

Expand All @@ -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<String , String> 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);
Expand All @@ -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;
}
}
Original file line number Diff line number Diff line change
@@ -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
) {
}

0 comments on commit 95f5faa

Please sign in to comment.