Spring boot 버전 2.7.7
RestTemplate을 이용해서 구현을 했고 다음에는 Feign Client 방식을 사용해볼 생각입니다.
@GetMapping("/kakao/callback")
@Operation(summary = "카카오 소셜 로그인 api", description = "callback 용도 api여서 swagger에서 test 안됩니다")
public ApiResponse<?> kakaoCallback(@RequestParam String code) {
String result = memberCommandService.kakaoAuth(code);
//반환한 카카오 프로필에서 기존 회원이면 jwt 토큰 반환 아니면 회원가입 진행
//토큰 반환은 쉽지만 회원가입 로직으로 보내야하면 false 반환해서 회원가입 진행하도록하기
if (result.startsWith("010")) {
String phone = result.substring(0, 11);
String email = result.substring(11);
return ApiResponse.onFailure("로그인 실패", "회원가입 필요", MemberConverter.toSocialJoinResultDTO(phone, email));
}
return ApiResponse.onSuccess(MemberConverter.toSocialLoginResultDTO(result));
}
@Transactional
public String kakaoAuth(String code) {
String jwt;
//Post방식으로 key=value 데이터를 요청
RestTemplate rt = new RestTemplate();
//HttpHeader 객체 생성
HttpHeaders headers = new HttpHeaders();
headers.add("Content-type", "application/x-www-form-urlencoded;charset=utf-8");
//HttpBody 객체 생성
MultiValueMap<String, String> params = new LinkedMultiValueMap<>();
params.add("grant_type", "authorization_code");
params.add("client_id", kakao_client_id);
params.add("redirect_uri", kakao_redirect_uri);
params.add("code", code);
//params.add("client_secret","");
//HttpHeader와 HttpBody를 하나의 객체로 담기
HttpEntity<MultiValueMap<String, String>> kakaoTokenRequest =
new HttpEntity<>(params, headers);
//Http 요청해서 응답 받음
ResponseEntity<String> response = rt.exchange(
"https://kauth.kakao.com/oauth/token",
HttpMethod.POST,
kakaoTokenRequest,
String.class
);
ObjectMapper objectMapper = new ObjectMapper();
OAuthToken oAuthToken = null;
try {
oAuthToken = objectMapper.readValue(response.getBody(), OAuthToken.class);
} catch (JsonProcessingException e) {
throw new RuntimeException(e);
}
///////////////////////////
//토큰 이용해서 정보 가져오기
RestTemplate rt2 = new RestTemplate();
HttpHeaders headers2 = new HttpHeaders();
headers2.add("Authorization", "Bearer " + oAuthToken.getAccess_token());
headers2.add("Content-type", "application/x-www-form-urlencoded;charset=utf-8");
//HttpHeader와 HttpBody를 하나의 객체로 담기
HttpEntity<MultiValueMap<String, String>> kakaoProfileRequest =
new HttpEntity<>(headers2);
ResponseEntity<String> response2 = rt2.exchange(
"https://kapi.kakao.com/v2/user/me",
HttpMethod.POST,
kakaoProfileRequest,
String.class
);
ObjectMapper objectMapper2 = new ObjectMapper();
KakaoProfile kakaoProfile = null;
try {
kakaoProfile = objectMapper2.readValue(response2.getBody(), KakaoProfile.class);
} catch (JsonProcessingException e) {
throw new RuntimeException(e);
}
String phone = kakaoProfile.kakao_account.phone_number.replaceAll("[^0-9]", "");
if (phone.startsWith("82")) {
phone = "0" + phone.substring(2);
}
Optional<Member> member = memberRepository.findByPhone(phone);
if (member.isPresent()) {
if(!this.checkDeregister(member.orElseThrow())){
throw new MemberHandler(ErrorStatus.MEMBER_INACTIVATE);
}
return tokenProvider.createAccessToken(member.get().getId(), member.get().getMemberRole().toString() , member.get().getEmail(), Arrays.asList(new SimpleGrantedAuthority("USER")));
}
return phone + kakaoProfile.getKakao_account().email;
}
@GetMapping("/naver/callback")
@Operation(summary = "네이버 소셜 로그인 api", description = "callback 용도 api여서 swagger에서 test 안됩니다")
public ApiResponse<?> naverCallback(@RequestParam String code, String state) {
String result = memberCommandService.naverAuth(code, state);
if (result.startsWith("010")) {
String phone = result.substring(0, 11);
String email = result.substring(11);
return ApiResponse.onFailure("로그인 실패", "회원가입 필요", MemberConverter.toSocialJoinResultDTO(phone, email));
}
return ApiResponse.onSuccess(MemberConverter.toSocialLoginResultDTO(result));
}
@Override
@Transactional
public String naverAuth(String code, String state) {
RestTemplate rt = new RestTemplate();
HttpHeaders headers = new HttpHeaders();
headers.add("Content-type", "application/x-www-form-urlencoded;charset=utf-8");
//HttpBody 객체 생성
MultiValueMap<String, String> params = new LinkedMultiValueMap<>();
params.add("grant_type", "authorization_code");
params.add("client_id", naver_client_id);
params.add("redirect_uri", naver_redirect_uri);
params.add("client_secret", naver_client_secret);
params.add("code", code);
params.add("state", state);
//HttpHeader와 HttpBody를 하나의 객체로 담기
HttpEntity<MultiValueMap<String, String>> naverTokenRequest =
new HttpEntity<>(params, headers);
//Http 요청해서 응답 받음
ResponseEntity<String> response = rt.exchange(
"https://nid.naver.com/oauth2.0/token",
HttpMethod.POST,
naverTokenRequest,
String.class
);
ObjectMapper objectMapper = new ObjectMapper();
OAuthToken oAuthToken = null;
try {
oAuthToken = objectMapper.readValue(response.getBody(), OAuthToken.class);
} catch (JsonProcessingException e) {
throw new RuntimeException(e);
}
RestTemplate rt2 = new RestTemplate();
HttpHeaders headers2 = new HttpHeaders();
headers2.add("Authorization", "Bearer " + oAuthToken.getAccess_token());
headers2.add("Content-type", "application/x-www-form-urlencoded;charset=utf-8");
//HttpHeader와 HttpBody를 하나의 객체로 담기
HttpEntity<MultiValueMap<String, String>> naverProfileRequest =
new HttpEntity<>(headers2);
ResponseEntity<String> response2 = rt2.exchange(
"https://openapi.naver.com/v1/nid/me",
HttpMethod.POST,
naverProfileRequest,
String.class
);
ObjectMapper objectMapper2 = new ObjectMapper();
NaverProfile naverProfile = null;
try {
naverProfile = objectMapper2.readValue(response2.getBody(), NaverProfile.class);
} catch (JsonProcessingException e) {
throw new RuntimeException(e);
}
String phone = naverProfile.response.mobile.replaceAll("[^0-9]", "");
Optional<Member> member = memberRepository.findByPhone(phone);
if (member.isPresent()) {
if(!this.checkDeregister(member.orElseThrow())){
throw new MemberHandler(ErrorStatus.MEMBER_INACTIVATE);
}
return tokenProvider.createAccessToken(member.get().getId(), member.get().getMemberRole().toString() , member.get().getEmail(), Arrays.asList(new SimpleGrantedAuthority("USER")));
}
return phone + naverProfile.getResponse().email;
}
문제 상황:
- local에서 테스트할 때는 잘 되었지만 redirect uri를 수정하면서 프론트에는 제대로 응답이 가질 않는다.
해결: redirect uri를 프론트 url로 설정 했어야하는데 callback을 보내는 url로 설정해서 오류 발생
문제상황:
- 프론트에서 카카오 서버나 네이버 서버로 요청을 보냈을때 cors 에러 발생
해결 : 카카오 application에서 test계정은 등록되어 있는 계정만 사용 가능해서 추가 등록해서 오류 해결