Springboot

[Springboot] 41. OAuth 2.0 (3단계-Kakao Developers 소셜 로그인 API 토큰 받기)

Song hyun 2024. 8. 16. 09:48
728x90
반응형

[Springboot] 41. OAuth 2.0 (3단계-Kakao Developers 소셜 로그인 API 토큰 받기)

 

1. 문서 살펴보기 

https://developers.kakao.com/docs/latest/ko/kakaologin/rest-api

 

Kakao Developers

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

developers.kakao.com

 

 

2. 토큰 받아오기

	
	@GetMapping("/kakao")
	@ResponseBody //  @RestController = @Controller + @ResponseBody
	public String getMethodName(@RequestParam(name="code") String code) {
		System.out.println("code : "+code);
		
		// POST - 카카오 토큰 요청 받기
		// header, body 구성
		RestTemplate rt1 = new RestTemplate();
		
		// 헤더 구성
		HttpHeaders header1 = new HttpHeaders();
		header1.add("Content-type", "application/x-www-form-urlencoded;charset=utf-8");
		
		// 바디 구성
		MultiValueMap<String, String> params1 = new LinkedMultiValueMap<String,String>();
		params1.add("grant_type", "authorization_code");
		params1.add("client_id", "e6c387d854fa01be9131e377baa4686f");
		params1.add("redirect_uri", "http://localhost:8080/user/kakao");
		params1.add("code", code);
		
		// 헤더+바디 결합
		HttpEntity<MultiValueMap<String,String>> reqKakaoMessage
			= new HttpEntity<>(params1,header1);
			
		// 통신 요청
		ResponseEntity<String> response= rt1.exchange("https://kauth.kakao.com/oauth/token", 
				HttpMethod.POST, reqKakaoMessage, String.class);
				
		System.out.println("response : "+response);
		
		return response.getBody().toString();
	}

 

 

 

3. jsonschema2pojo 사용하기

https://www.jsonschema2pojo.org/

 

jsonschema2pojo

Reference properties For each property present in the 'properties' definition, we add a property to a given Java class according to the JavaBeans spec. A private field is added to the parent class, along with accompanying accessor methods (getter and sette

www.jsonschema2pojo.org

 


package com.tenco.bank.dto;

import java.util.LinkedHashMap;
import java.util.Map;

import com.fasterxml.jackson.annotation.JsonAnyGetter;
import com.fasterxml.jackson.annotation.JsonAnySetter;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonPropertyOrder;

@JsonInclude(JsonInclude.Include.NON_NULL)
@JsonPropertyOrder({
    "access_token",
    "token_type",
    "refresh_token",
    "expires_in",
    "scope",
    "refresh_token_expires_in"
})
public class OAuthToken {

    @JsonProperty("access_token")
    private String accessToken;
    @JsonProperty("token_type")
    private String tokenType;
    @JsonProperty("refresh_token")
    private String refreshToken;
    @JsonProperty("expires_in")
    private Integer expiresIn;
    @JsonProperty("scope")
    private String scope;
    @JsonProperty("refresh_token_expires_in")
    private Integer refreshTokenExpiresIn;
    @JsonIgnore
    private Map<String, Object> additionalProperties = new LinkedHashMap<String, Object>();

    @JsonProperty("access_token")
    public String getAccessToken() {
        return accessToken;
    }

    @JsonProperty("access_token")
    public void setAccessToken(String accessToken) {
        this.accessToken = accessToken;
    }

    @JsonProperty("token_type")
    public String getTokenType() {
        return tokenType;
    }

    @JsonProperty("token_type")
    public void setTokenType(String tokenType) {
        this.tokenType = tokenType;
    }

    @JsonProperty("refresh_token")
    public String getRefreshToken() {
        return refreshToken;
    }

    @JsonProperty("refresh_token")
    public void setRefreshToken(String refreshToken) {
        this.refreshToken = refreshToken;
    }

    @JsonProperty("expires_in")
    public Integer getExpiresIn() {
        return expiresIn;
    }

    @JsonProperty("expires_in")
    public void setExpiresIn(Integer expiresIn) {
        this.expiresIn = expiresIn;
    }

    @JsonProperty("scope")
    public String getScope() {
        return scope;
    }

    @JsonProperty("scope")
    public void setScope(String scope) {
        this.scope = scope;
    }

    @JsonProperty("refresh_token_expires_in")
    public Integer getRefreshTokenExpiresIn() {
        return refreshTokenExpiresIn;
    }

    @JsonProperty("refresh_token_expires_in")
    public void setRefreshTokenExpiresIn(Integer refreshTokenExpiresIn) {
        this.refreshTokenExpiresIn = refreshTokenExpiresIn;
    }

    @JsonAnyGetter
    public Map<String, Object> getAdditionalProperties() {
        return this.additionalProperties;
    }

    @JsonAnySetter
    public void setAdditionalProperty(String name, Object value) {
        this.additionalProperties.put(name, value);
    }

}

 

 

4. OAuthToken (Oauth DTO 만들기)

*@JsonNaming


package com.tenco.bank.dto;
import com.fasterxml.jackson.databind.PropertyNamingStrategies;
import com.fasterxml.jackson.databind.annotation.JsonNaming;

import lombok.Data;
import lombok.ToString;

// JSON 형식에 
// 코딩 컨벤션을 스네이크 케이스에서 카멜 노테이션으로 할당하라

@Data
@ToString
@JsonNaming(value = PropertyNamingStrategies.SnakeCaseStrategy.class)
public class OAuthToken {

    private String accessToken;
    private String tokenType;
    private String refreshToken;
    private Integer expiresIn;
    private String scope;
    private Integer refreshTokenExpiresIn;

}
package com.tenco.bank.controller;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.util.UriComponentsBuilder;

import com.tenco.bank.utils.Define;
import com.tenco.bank.dto.OAuthToken;
import com.tenco.bank.dto.SignInDTO;
import com.tenco.bank.dto.SignUpDTO;
import com.tenco.bank.handler.exception.DataDeliveryException;
import com.tenco.bank.repository.model.User;
import com.tenco.bank.service.UserService;

import jakarta.servlet.http.HttpSession;

@Controller // IoC에 대상(싱글톤 패턴으로 관리됨) 
@RequestMapping("/user") // 대문 처리 
public class UserController {

	private UserService userService;
	private final HttpSession session;
	
	// DI 처리 
	@Autowired // 노란색 경고는 사용할 필요 없음 - 가독성 위해서 선언해도 됨
	public UserController(UserService service, HttpSession session) {
		this.userService = service;
		this.session = session;
	}
	
	/**
	 * 회원 가입 페이지 요청 
	 * 주소 설계 : http://localhost:8080/user/sign-up
	 * @return signUp.jsp 
	 */
	@GetMapping("/sign-up")
	public String signUpPage() {
		// prefix: /WEB-INF/view/
		// return: user/signUp
		// suffix: .jsp 
		return "user/signUp";
	}
	
	/**
	 * 회원 가입 로직 처리 요청
	 * 주소 설계 : http://localhost:8080/user/sign-up
	 * @param dto
	 * @return
	 */
	@PostMapping("/sign-up")
	public String signUpProc(SignUpDTO dto) {
		// controller 에서 일반적이 코드 작업 
		// 1. 인증검사 (여기서는 인증검사 불 필요) 
		// 2. 유효성 검사 
		
		if(dto.getUsername() == null || dto.getUsername().isEmpty()) {
			throw new DataDeliveryException(Define.ENTER_YOUR_USERNAME, HttpStatus.BAD_REQUEST);
		}
		
		if(dto.getPassword() == null || dto.getPassword().isEmpty()) {
			throw new DataDeliveryException(Define.ENTER_YOUR_PASSWORD, HttpStatus.BAD_REQUEST);
		}
		
		if(dto.getFullname() == null || dto.getFullname().isEmpty()) {
			throw new DataDeliveryException(Define.ENTER_YOUR_FULLNAME, HttpStatus.BAD_REQUEST);
		}
		userService.createUser(dto);
		return "redirect:/user/sign-in";
	}
	
	/*
	 * 로그인 화면 요청 
	 * 주소설계 : http://localhost:8080/user/sign-in
	 */
	@GetMapping("/sign-in")
	public String singInPage() {
		// 인증검사  x 
		// 유효성검사 x  
		return "user/signIn";
	}
	
	
	/**
	 * 로그인 요청 처리 
	 * 주소설계 : http://localhost:8080/user/sign-in
	 * @return
	 */
	@PostMapping("/sign-in")
	public String signProc(SignInDTO dto) {
		// 1. 인증 검사 x 
		// 2. 유효성 검사 
		
		if(dto.getUsername() == null || dto.getUsername().isEmpty()) {
			throw new DataDeliveryException(Define.ENTER_YOUR_USERNAME, HttpStatus.BAD_REQUEST);
		}
		if(dto.getPassword() == null || dto.getPassword().isEmpty()) {
			throw new DataDeliveryException(Define.ENTER_YOUR_PASSWORD, HttpStatus.BAD_REQUEST);
		}
		// 서비스 호출 
		User principal = userService.readUser(dto);
		// 세션 메모리에 등록 처리 
		session.setAttribute(Define.PRINCIPAL, principal);
		// 새로운 페이지로 이동 처리 
		// TODO - 계좌 목록 페이지 이동처리 예정 
		return "redirect:/index"; 
	}
	
	// 코드 추가 
	@GetMapping("/logout")
	public String logout() {
		session.invalidate(); // 로그아웃 됨 
		return "redirect:/user/sign-in";
	}
	
	@GetMapping("/kakao")
	@ResponseBody //  @RestController = @Controller + @ResponseBody
	public String getMethodName(@RequestParam(name="code") String code) {
		System.out.println("code : "+code);
		
		// POST - 카카오 토큰 요청 받기
		// header, body 구성
		RestTemplate rt1 = new RestTemplate();
		
		// 헤더 구성
		HttpHeaders header1 = new HttpHeaders();
		header1.add("Content-type", "application/x-www-form-urlencoded;charset=utf-8");
		
		// 바디 구성
		MultiValueMap<String, String> params1 = new LinkedMultiValueMap<String,String>();
		params1.add("grant_type", "authorization_code");
		params1.add("client_id", "e6c387d854fa01be9131e377baa4686f");
		params1.add("redirect_uri", "http://localhost:8080/user/kakao");
		params1.add("code", code);
		
		// 헤더+바디 결합
		HttpEntity<MultiValueMap<String,String>> reqKakaoMessage
			= new HttpEntity<>(params1,header1);
			
		// 통신 요청
		ResponseEntity<OAuthToken> response= rt1.exchange("https://kauth.kakao.com/oauth/token", 
				HttpMethod.POST, reqKakaoMessage, OAuthToken.class);
				
		System.out.println("response : "+response.getBody().toString());
		
		return response.getBody().toString();
	}
	
	
}

OAuthToken에 토큰들이 담겨 온 것을 확인할 수 있다.

728x90
반응형