프로젝트 🧸/CocO 🐤

[Spring Boot] Kakao 소셜 로그인 구현: OAuth2.0 & Spring Security 활용 🐤

코양이🤍 2024. 9. 11. 15:46

구글 소셜 로그인을 구현했던 이전글에 이어서 카카오 소셜 로그인을 구현해보도록 하겠습니다.

구글 소셜 로그인 구현 방법은 아래 글에서 확인할 수 있습니다

 

[Spring Boot] Google 소셜 로그인 구현: OAuth2.0와 Spring Security를 활용한 완벽 가이드 🐤

소셜 로그인은 사용자에게 간편한 로그인 방법을 제공하고, 개발자는 사용자 인증을 쉽게 처리할 수 있는 방법입니다. 이번 글에서는 OAuth2.0와 Spring Security를 활용해 구글 소셜 로그인을 구현하

blu-blu.tistory.com


⭐ 코드 설명

✅ Service 폴더 - Social 폴더 

☑️ KakaoOauth.java 파일

package com.example.CocO.service.social;

import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Component;
import org.springframework.web.client.RestTemplate;

import java.io.BufferedOutputStream;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.HashMap;
import java.util.Map;
import java.util.stream.Collectors;

@Slf4j
@Component
@RequiredArgsConstructor
public class KakaoOauth implements SocialOauth {

    @Value("${sns.kakao.url}")
    private String KAKAO_SNS_BASE_URL;
    @Value("${sns.kakao.client.id}")
    private String KAKAO_SNS_CLIENT_ID;
    @Value("${sns.kakao.callback.url}")
    private String KAKAO_SNS_CALLBACK_URL;
    @Value("${sns.kakao.client.secret}")
    private String KAKAO_SNS_CLIENT_SECRET;
    @Value("${sns.kakao.token.url}")
    private String KAKAO_SNS_TOKEN_BASE_URL;

    @Override
    public String getOauthRedirectURL() {
        Map<String, Object> params = new HashMap<>();
        params.put("response_type", "code");
        params.put("client_id", KAKAO_SNS_CLIENT_ID);
        params.put("redirect_uri", KAKAO_SNS_CALLBACK_URL);

        String parameterString = params.entrySet().stream()
                .map(x -> x.getKey() + "=" + x.getValue())
                .collect(Collectors.joining("&"));

        return KAKAO_SNS_BASE_URL + "?" + parameterString;
    }

    @Override
    public String requestAccessToken(String code) {
        RestTemplate restTemplate = new RestTemplate();

        Map<String, Object> params = new HashMap<>();
        params.put("code", code);
        params.put("client_id", KAKAO_SNS_CLIENT_ID);
        params.put("client_secret", KAKAO_SNS_CLIENT_SECRET);
        params.put("redirect_uri", KAKAO_SNS_CALLBACK_URL);
        params.put("grant_type", "authorization_code");

        ResponseEntity<String> responseEntity =
                restTemplate.postForEntity(KAKAO_SNS_TOKEN_BASE_URL, params, String.class);

        if (responseEntity.getStatusCode() == HttpStatus.OK) {
            return responseEntity.getBody();
        }
        return "카카오 로그인 요청 처리 실패";
    }

    public String requestAccessTokenUsingURL(String code) {
        try {
            URL url = new URL(KAKAO_SNS_TOKEN_BASE_URL);
            HttpURLConnection conn = (HttpURLConnection) url.openConnection();
            conn.setRequestMethod("POST");
            conn.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
            conn.setDoOutput(true);

            Map<String, Object> params = new HashMap<>();
            params.put("code", code);
            params.put("client_id", KAKAO_SNS_CLIENT_ID);
            params.put("client_secret", KAKAO_SNS_CLIENT_SECRET);
            params.put("redirect_uri", KAKAO_SNS_CALLBACK_URL);
            params.put("grant_type", "authorization_code");

            String parameterString = params.entrySet().stream()
                    .map(x -> x.getKey() + "=" + x.getValue())
                    .collect(Collectors.joining("&"));

            BufferedOutputStream bous = new BufferedOutputStream(conn.getOutputStream());
            bous.write(parameterString.getBytes());
            bous.flush();
            bous.close();

            BufferedReader br = new BufferedReader(new InputStreamReader(conn.getInputStream()));

            StringBuilder sb = new StringBuilder();
            String line;

            while ((line = br.readLine()) != null) {
                sb.append(line);
            }

            if (conn.getResponseCode() == 200) {
                return sb.toString();
            }
            return "카카오 로그인 요청 처리 실패";
        } catch (IOException e) {
            throw new IllegalArgumentException("알 수 없는 카카오 로그인 Access Token 요청 URL 입니다 :: " + KAKAO_SNS_TOKEN_BASE_URL);
        }
    }
}

OAuth 리다이렉트 URL 생성

getOauthRedirectURL() 메서드

 

  • 이 메서드는 사용자가 카카오 로그인 버튼을 눌렀을 때, 카카오 로그인 페이지로 리다이렉트하기 위한 URL을 생성합니다.
  • 카카오 인증 서버로 요청을 보내기 위한 파라미터들을 Map에 담은 후, 이를 key=value 형식으로 변환해 URL에 붙여 리턴합니다.
  • 파라미터로는 response_type(고정값 "code"), client_id, redirect_uri를 사용하며, 이는 카카오 OAuth에서 인증 코드를 받기 위한 기본적인 정보입니다.

액세스 토큰 요청

 

카카오 인증을 완료한 후, 받은 인증 코드를 바탕으로 액세스 토큰을 요청하는 두 가지 방법이 구현되어 있습니다.

A. RestTemplate을 사용한 방식

requestAccessToken(String code) 메서드

 

  • 스프링의 RestTemplate을 이용하여 HTTP POST 요청을 보내고, 액세스 토큰을 요청하는 메서드입니다.
  • 인증 서버로 보낼 파라미터들을 Map으로 구성한 후, RestTemplate의 postForEntity 메서드를 사용하여 토큰 요청을 처리합니다.
  • 요청이 성공하면 응답의 HTTP 상태 코드를 확인한 후, 성공적인 경우 응답 본문에 포함된 데이터를 반환합니다. 실패하면 "카카오 로그인 요청 처리 실패" 메시지를 반환합니다.

B. HttpURLConnection을 사용한 방식

requestAccessTokenUsingURL(String code) 메서드

  • HttpURLConnection을 직접 사용하여 카카오 서버에 POST 요청을 보내고, 액세스 토큰을 요청하는 메서드입니다.
  • URL 객체를 생성하고, 연결을 설정한 후, 파라미터를 전송합니다. 이후 응답 스트림을 읽어 결과를 처리합니다.
  • 응답 코드가 200일 경우 정상적으로 토큰 정보를 반환하고, 그렇지 않으면 "카카오 로그인 요청 처리 실패" 메시지를 반환합니다.
  • 이 방식은 RestTemplate보다 더 저수준의 HTTP 요청 처리를 직접 다루는 방식으로, HTTP 연결을 수동으로 설정하고, 출력 스트림과 입력 스트림을 사용해 데이터를 전송 및 수신합니다.

 

✅ resources - application.properties 파일

sns.kakao.url=https://kauth.kakao.com/oauth/authorize
sns.kakao.client.id=<카카오 개발자 센터에서 발급받은 REST API Key>
sns.kakao.client.secret=<카카오 개발자 센터에서 발급받은 클라이언트 Secret>
sns.kakao.callback.url=http://localhost:8080/auth/kakao/callback
sns.kakao.token.url=https://kauth.kakao.com/oauth/token

 

 


 

⭐ 테스트

이제 Spring Boot 서버를 구동한 후, 카카오소셜 로그인을 테스트할 수 있습니다. 

먼저, Spring Boot 서버를 실행합니다. 저는 현재 로컬에서 개발 중이어서 localhost:8080로 실행되고 있습니다.

크롬과 같은 웹 브라우저를 사용해서 테스트 한다면 아래 링크에서 확인할 수 있습니다.

 http://localhost:8080/auth/kakao

혹은 Postman과 같은 API 도구를 사용한다면 아래와 같이 요청을 보냅니다.

GET http://localhost:8080/auth/kakao

요청을 보내면 콘솔 로그에서 다음과 같은 결과를 확인할 수 있습니다.


📌 참고

 

Kakao Developers

카카오 API를 활용하여 다양한 어플리케이션을 개발해보세요. 카카오 로그인, 메시지 보내기, 친구 API, 인공지능 API 등을 제공합니다.

developers.kakao.com