


동의 항목들을 활성화 해주자

발급된 키들을 기억해둔다 (외부의 노출되서는 안된다!)

사이트 도메인 및 리다이렉트 URI를 등록해준다. 프론트는 구현이 안되기에 도메인은 실질적으로 하는일은 없다.


리다이렉트 URI 는 사용자가 카카오인증을 끝내고 인가코드를 전달받는 URI 로 APP이 카카오 서버의 접근이 가능한 인가코드 를 받을 수 있는 URI로 리다이렉트된다.
public class AuthController {
@Value("${oauth2.kakao.client-secret}")
private String kakaoKey;
@GetMapping("/{provider}/login")
public RedirectView socialLogin(@PathVariable String provider) {
if ("kakao".equals(provider)) {
String redirectUrl =
"https://kauth.kakao.com/oauth/authorize"
+ "?client_id="
+ kakaoKey
+ "&redirect_uri="
+ "http://localhost:8080/api/oauth2/kakao/callback"
+ "&response_type=code";
return new RedirectView(redirectUrl);
}
throw new IllegalArgumentException("Unsupported provider: " + provider);
}
}


/api/oauth2/kakao/login 에 접근하면 해당 메서드가 호출RedirectView 를 통해 브라우저가 카카오 로그인페이지로 이동하도록 한다.http://localhost:8080/api/oauth2/kakao/callback URL 로 리다이렉트 시켜준다.액세스토큰 이 필요하다.
HTTP Headers 에 담아서 보내줄 필요가 있다. private String getKakaoAccessToken(String code) throws JsonProcessingException {
HttpHeaders headers = new HttpHeaders();
headers.add("Content-type", "application/x-www-form-urlencoded;charset=utf-8");
MultiValueMap<String, String> body = new LinkedMultiValueMap<>();
body.add("grant_type", "authorization_code");
body.add("client_id", kakaoKey);
body.add("redirect_uri","http://localhost:8080/api/oauth2/kakao/callback");
body.add("code", code);
HttpEntity<MultiValueMap<String, String>> request = new HttpEntity<>(body, headers);
ResponseEntity<String> response = restTemplate.postForEntity("https://kauth.kakao.com/oauth/token", request, String.class);
ObjectMapper mapper = new ObjectMapper();
JsonNode node = mapper.readTree(response.getBody());
return node.get("access_token").asText();
}
HttpEntity 를 생성해 카카오가 요구하는 값들을 MultiValueMap 의 형태로 담아준다.restTemplate 을 통해 완성된 HttpEntity 를 post 방식으로 카카오 인증서버인 https://kauth.kakao.com/oauth/token 로 전달 이후 String 타입으로 값을 받음.ObjectMapper 를 통해 String->JSON 변환하여 내부에 있는 access_token 을 추출한다.

@Getter
@Builder
@NoArgsConstructor
public class KakaoUserInfoDto {
private Long id;
private String nickname;
private String email;
public KakaoUserInfoDto(Long id, String nickname, String email) {
this.id = id;
this.nickname = nickname;
this.email = email;
}
}
private KakaoUserInfoDto getKakaoUserInfo(String accessToken) throws JsonProcessingException {
HttpHeaders headers = new HttpHeaders();
headers.add("Authorization", "Bearer " + accessToken);
headers.add("Content-type", "application/x-www-form-urlencoded;charset=utf-8");
RequestEntity<MultiValueMap<String, String>> requestEntity =
RequestEntity.post("https://kapi.kakao.com/v2/user/me")
.headers(headers)
.body(new LinkedMultiValueMap<>());
ResponseEntity<String> response = restTemplate.exchange(requestEntity, String.class);
JsonNode jsonNode = new ObjectMapper().readTree(response.getBody());
Long id = jsonNode.get("id").asLong();
String nickname = jsonNode.get("properties").get("nickname").asText();
String email = jsonNode.get("kakao_account").get("email").asText();
return new KakaoUserInfoDto(id, nickname, email);
}
accessToken 을 담아 URI 를 통해 접근 한다.{
"id": 1632335751,
"properties": {
"nickname": "르탄이",
"profile_image": "http://k.kakaocdn.net/...jpg",
"thumbnail_image": "http://k.kakaocdn.net/...jpg"
},
"kakao_account": {
"profile_needs_agreement": false,
"profile": {
"nickname": "르탄이",
"thumbnail_image_url": "http://k.kakaocdn.net/...jpg",
"profile_image_url": "http://k.kakaocdn.net/...jpg"
},
"has_email": true,
"email_needs_agreement": false,
"is_email_valid": true,
"is_email_verified": true,
"email": "letan@sparta.com"
}
}
KakaoUserInfoDto 를 통해 데이터를 담아준다.
private Member registerSocialMemberIfNeeded(UserInfoDto userInfo) {
Member socialMember = memberRepository.findByProviderId(userInfo.getId()).orElse(null);
if (socialMember != null) {
return socialMember;
}
if (memberRepository.existsByEmail(userInfo.getEmail())) {
throw new IllegalArgumentException("이메일이 이미 등록되어 있습니다. 다른 소셜 로그인으로 로그인해주세요.");
}
Member member =
Member.builder()
.email(userInfo.getEmail())
.providerId(userInfo.getId())
.provider(userInfo.getProvider())
.nickname(userInfo.getNickname())
.build();
memberRepository.save(member);
return member;
}


참조 :
https://devlopsquare.tistory.com/188
https://kangcoder.tistory.com/7
https://velog.io/@wlsgur1533/%EC%8A%A4%ED%94%84%EB%A7%81-%EC%8B%9C%ED%81%90%EB%A6%AC%ED%8B%B0-%EC%97%86%EC%9D%B4-OAuth-%EA%B5%AC%ED%98%84%ED%95%98%EA%B8%B0#10-2-naver-%EB%A1%9C%EA%B7%B8%EC%9D%B8-%ED%98%B8%EC%B6%9C