티스토리 뷰

스프링 부트 REST API WEB 프로젝트

깃헙 링크

https://github.com/choiwoonsik/springboot_RestApi_App_Project/tree/main/restApiSpringBootApp

수행 목록

  1. 환경구성 및 helloworld 출력
  2. H2 DB 연동
  3. Swagger API 문서 연동
  4. REST API 설계
  5. RestControllerAdvice를 이용한 통합 예외 처리
  6. Entity - DTO 분리
  7. MessageSource를 이용해 예외 메시지 다국화
  8. JPA Aduting을 이용해 객체 생성시간/수정시간 적용
  9. 스프링 시큐리티 + Jwt를 이용해서 인증 및 권한 체크
  10. 스프링 시큐리티 AuthenticationEntryPoint, AccessDenied로 인증 및 인가 예외처리
  11. Jwt AccessToken + RefreshToken으로 보안성과 사용자 편의성 고도화하기
  12. JUnit Test (단위 테스트)
  13. JUnit Test (통합 테스트)
  14. OAuth 2.0 정리
  15. OAuth 2.0 카카오 로그인 part.1 Authorization code + Token 발급
  16. OAuth 2.0 카카오 로그인 part.2 토큰으로 회원 가입 / 로그인
  17. OAuth 2.0 카카오 로그인 테스트 검증
  18. 환경별 설정을 위해서profile 분리하기 

앞에서 정리한 OAuth 2.0을 바탕으로 직접 구현해본다

카카오 OAuth 로직

위와 같은 로직으로 카카오 로그인이 돌아간다.

 

카카오 로그인 사용을 위한 작업

  1. 카카오 Developers 사이트에 가서 자신의 APP을 생성
  2. 로그인 페이지 / 콜백 페이지 연동
  3. AccessToken을 통해 가입 및 로그인

 

1. 카카오 Developers 설정

웹 앱의 주소 등록 : 앱설정 -> 플랫폼

콜백 주소 (Redirect URI) 등록 : 카카오 로그인


2.  로그인 페이지 / 콜백 페이지 구현

카카오와 통신이 필요하므로 RestTemplate를 빈으로 등록

@Bean
public RestTemplate getRestTemplate() {
	return new RestTemplate();
}

 

카카오 연동 결과 Json을 객체로 매핑하기 위해 Gson 라이브러리를 사용. 의존성 추가

// Json을 결과로 매핑하기 위한 의존성
implementation 'com.google.code.gson:gson'

 

2- 1. 카카오 api와 연동하기 위한 설정정보 application.yml 작성

인가코드받기

GET /oauth/authorize?client_id={REST_API_KEY}&redirect_uri={REDIRECT_URI}&response_type=code HTTP/1.1
Host: kauth.kakao.com
https://kauth.kakao.com/oauth/authorize?response_type=code&client_id={REST_API_KEY}&redirect_uri={REDIRECT_URI}

토큰받기

POST /oauth/token HTTP/1.1
Host: kauth.kakao.com
Content-type: application/x-www-form-urlencoded;charset=utf-8

사용자정보 가져오기

GET/POST /v2/user/me HTTP/1.1
Host: kapi.kakao.com
Authorization: Bearer {ACCESS_TOKEN}
Content-type: application/x-www-form-urlencoded;charset=utf-8

사용자 연결 끊기 (재동의를 위해 구현)

POST /v1/user/unlink HTTP/1.1
Host: kapi.kakao.com
Authorization: Bearer {ACCESS_TOKEN}

 

위 내용을 가지고 application.yml을 작성

social:
  kakao:
    client-id: {REST_API_PRIVATE_KEY}
    redirect: /oauth/kakao/redirect
    url:
      login: https://kauth.kakao.com/oauth/authorize
      token: https://kauth.kakao.com/oauth/token
      profile: https://kapi.kakao.com/v2/user/me
      unlink: https://kapi.kakao.com/v1/user/unlink
url:
  base: http://localhost:8080
  1. [http://localhost:8080/oauth/kakao/login]로 접속
  2. 카카오 로그인 버튼이 있는 창을 띄어줌, 해당 버튼을 누르면 요청 URL을 보내고 카카오 로그인 창이 뜨게 된다.
  3. 요청 URL은 [인가 코드 받기]에 있는 URL이다.
  • 요청 URL 형식 : https://kauth.kakao.com/oauth/authorize?response_type=code&client_id={REST_API_KEY}&redirect_uri={REDIRECT_URI}

 

2- 2.  카카오 인가 코드 받기

카카오 서버에 나의 {REST_API_KEY} 와 {REDIRECT_URI}를 담아서 GET 요청
    @Value("${url.base}")
    private String baseUrl;

    @Value("${social.kakao.client-id}")
    private String kakaoClientId;

    @Value("${social.kakao.redirect}")
    private String kakaoRedirectUri;

    @GetMapping("/login")
    public ModelAndView socialLogin(ModelAndView mav) {

        StringBuilder loginUri = new StringBuilder()
                .append(env.getProperty("social.kakao.url.login"))
                .append("?response_type=code")
                .append("&client_id=").append(kakaoClientId)
                .append("&redirect_uri=").append(baseUrl).append(kakaoRedirectUri);
        mav.addObject("loginUrl", loginUri);
        mav.setViewName("social/login");
        return mav;
    }

 

카카오톡 로그인 버튼을 갖는 view 만듬 : resources/templates/social/login.ftl
<button onclick="popupKakaoLogin()">KakaoLogin</button>
<script>
    function popupKakaoLogin() {
        window.open('${loginUrl}', 'popupKakaoLogin', 'width=700,height=500,scrollbars=0,toolbar=0,menubar=no')
    }
</script>
  • 사용자가 카카오 로그인 버튼을 누르면
  • -> 위에서 작성한 loginUri로 요청이 가고 카카오톡 로그인 창이 뜸
  • -> 사용자는 로그인 후 동의항목을 체크하고 진행
  • -> 이때 URL은 client_idredirect_uri를 가지고 있고, 카톡 서버가 검증 후 redirect_uri로 사용자의 Authorization_code (인가 코드)를 쿼리 파라미터로 포함해서 리다이렉트 한다. (이때 카카오는 카카오의 회원정보를 나의 앱과 연동시킨다.)

카카오 로그인 시 redirect_uri, client_id가 포함되어 있다.

카카오가 돌려준 AuthorizationCode를 가지고 토큰 받기

redirect_uri는 내가 미리 카톡 API 서버에 지정해둔 것이므로 해당 리다이렉트 주소에 맞게 처리하면 된다.

    @GetMapping(value = "/redirect")
    public ModelAndView redirectKakao(
            ModelAndView mav,
            @ApiParam(value = "Authorization Code", required = true)
            @RequestParam String code) {

        mav.addObject("authInfo", kakaoService.getKakaoTokenInfo(code));
        mav.setViewName("social/redirectKakao");
        return mav;
    }

쿼리 파라미터에서 [인가 코드]를 얻어서 카카오에게 토큰을 요청한다.

  • kakaoService.getKakaoTokenInfo(code) 메소드를 작성하자

 

2-3. 카카오 토큰

  • [grant_type, 인가 코드, client_id, redirect_uri] 를 포함해서 카카오에 POST 요청해주면 카카오가 토큰을 돌려준다.
  • 해당 토큰을 매핑하기 위한 RetKakaoOAuth객체를 만든다.
RetKakaoOAuth 객체
package com.restApi.restApiSpringBootApp.dto.social;

@Getter
public class RetKakaoOAuth {
    private String token_type;
    private String access_token;
    private Integer expires_in;
    private String refresh_token;
    private String refresh_token_expires_in;
    private String scope;
}

❗️이때 gson을 이용하기위해 객체의 프로퍼티는 json의 key-value의 key이름과 동일하게 작성한다❗️

 

2.4 전달받은 [인가코드]로 [Token] 발급받기

  • [grant_type, 인가 코드, client_id, redirect_uri]를 담아서 POST 요청을 보낸다.
  • 정상적인 경우 Token을 전달받을 수 있다. 토큰 값을 RetKakaoOAuth객체로 매핑한다.

 

카카오 토큰 데이터를 RestKakaoToken으로 매핑
    public RetKakaoOAuth getKakaoTokenInfo(String code) {
        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);

        MultiValueMap<String, String> params = new LinkedMultiValueMap<>();
        params.add("grant_type", "authorization_code");
        params.add("client_id", kakaoClientId);
        params.add("redirect_uri", baseUrl + kakaoRedirectUri);
        params.add("code", code);

        String requestUri = env.getProperty("social.kakao.url.token");
        if (requestUri == null) throw new CCommunicationException();

        HttpEntity<MultiValueMap<String, String>> request = new HttpEntity<>(params, headers);
        ResponseEntity<String> response = restTemplate.postForEntity(requestUri, request, String.class);

        if (response.getStatusCode() == HttpStatus.OK)
            return gson.fromJson(response.getBody(), RetKakaoOAuth.class);
        throw new CCommunicationException();
    }

응답이 제대로 왔다면

  • gson을 이용해서 전달받은 Json 데이터를 ReKakaoOAuth로 매핑한다.
  • 이때 gson을 이용하려면 객체의 프로퍼티는 json의 key-value의 key이름과 동일해야한다.

 

발급받은 토큰 확인을 위한 view : resources/templates/social/redirectKakao.ftl
<ol>
    <li>token_type : ${authInfo.token_type}</li>
    <li>access_token : ${authInfo.access_token}</li>
    <li>expires_in : ${authInfo.expires_in}</li>
    <li>refresh_token : ${authInfo.refresh_token}</li>
    <li>refresh_token_expires_in : ${authInfo.refresh_token_expires_in}</li>
    <li>scope : ${authInfo.scope}</li>
</ol>

 

결과적으로 유저는 카카오 로그인을 통해 카카오로부터 Token을 발급 받았다. 이제 이 토큰을 이용해서 카카오에게 유저 정보를 요청하고 해당 유저 정보가 유효하다면, 그 정보를 이용해서 회원가입 시키면 된다.
-
내용이 길어져서 끊고 바로 다음 장에서 이어서 진행
반응형
Comments
반응형
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday