231025 TIL #225 OAuth / Kakao 소셜 로그인

김춘복·2023년 10월 25일
0

TIL : Today I Learned

목록 보기
225/488

Today I Learned

오늘은 JWT에 이어 OAuth와 소셜 로그인을 추가로 공부해보았다.


OAuth

  • Open Standard for Authorization
    개방형 Authorization의 표준.
    API 허가(Authorize)를 목적으로 JSON 형식으로 개발된 HTTP 기반의 보안 프로토콜.
    사용자가 사용하려하는 웹 및 앱에 비밀번호를 제공하지 않고 접근 권한을 부여받게 해주는 수단.

  • OAuth 2.0

    다양한 클라이언트 환경에 적합한 인증 및 인가의 위임방법을 제공하고 그 결과로 클라이언트에게 접근 토큰(Access Token)을 발급하는 것에대한 구조

소셜 로그인

  • 모든 웹사이트에서 회원가입을 하는 것은 부담스럽다. 회원입장에서도 서버 입장에서도.
    이런 문제를 해결하기 위해 OAuth를 사용한 소셜 로그인이 등장.

  • 카카오 로그인 흐름

인증요청 -> 토큰요청 -> 토큰으로 API 호출해서 카카오 사용자의 정보를 받음.

  • 카카오 로그인 애플리케이션 등록
    https://developers.kakao.com/console/app 에 접속해서
    애플리케이션 등록 후 플랫폼 설정에 사이트 도메인 등록(지금은 로컬 환경 서버 주소).
    그 다음 로그인했을때 다시 되받을 redirect URI를 등록하고 활성화 on.
    왼쪽 탭의 '동의 항목'을 눌러서 카카오 사용자의 어떤 정보를 가져올껀지 설정.

  • 카카오 로그인 설정
    일단 동의항목에 닉네임, email만 체크했으면 2가지와 kakaoId까지 3개의 정보를 받게된다.
    양식은 아래와 같이 JSON의 형태로 카카오에서 오게 된다.

{
  "id": 1632335421,
  "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"
  }
}

회원가입 테이블 설계 방법

  1. KakaoUser 테이블을 새로 만들어 카카오 User를 따로 관리한다.
    장점 : 결합도가 낮아진다. 성격이 다른 유저를 분리해 각 테이블의 변화에 서로 영향을 주지 않게되고 각 유저에 특성에 맞게 설계를 할 수 있다.
    단점 : 구현 난이도가 올라간다. 관심상품 설계시 일반유저와 카카오유저를 따로 구현해야 한다.

  2. 기존 회원 테이블에 카카오 User 추가
    장점 : 구현이 단순하다.
    단점 : 결합도가 높아진다. 일반 로그인탭으로 카카오 로그인 사용자의 username과 password로 로그인하면 에러가 발생할 수 있다.

실습에서는 2의 방법으로 진행하면서
User 테이블에 kakaoId 컬럼을 추가하되 nullable = true로 설정.

  • User Table
Column nametype중복허용설명카카오회원
idLongX테이블 ID (PK)테이블 ID
usernameStringX회원 IDnickname
passwordStringO패스워드UUID (랜덤으로 생성한 문자열)
emailStringX이메일 주소email
roleStringO역할
kakaoIdString (Nullable)X카카오 로그인 IDkakaoId

카카오에서 받은 nickname, email, kakaoId는 맞는 곳에 넣고
password는 일반 로그인을 하지 못하게 UUID 랜덤 문자열을 생성해서 삽입.


카카오 서버에서 인가코드 받기

카카오 인가 코드 받기 매뉴얼

  • 인가코드 요청 방법
https://kauth.kakao.com/oauth/authorize?client_id={REST_API_KEY}&redirect_uri={REDIRECT_URI}&response_type=code

여기서 발급받은 REST_API_KEY를 넣고 REDIRECT_URI를 넣은 다음
이 주소를 login.html의 '카카오로 로그인' 버튼에 onclick 링크로 달아두면 된다.

그리고 REDIRECT_URI로 카카오가 보낸 인가 코드를 받아주는 API를 Controller에 만든다.
GET 방식으로 들어오고, code는 쿼리스트링으로 들어오기때문에 @RequestParam으로 받는다.
Service에서 code를 가지고 카카오 로그인 처리를 하고 로그인 성공시 JWT를 반환한다.


KakaoService login 구현

html에서 인증코드를 요청해서 컨트롤러에서 받아왔기 때문에 서비스에서는 이 인증코드로 카카오에 토큰을 요청해서 받고 토큰으로 API를 호출해서 response를 받아 클라이언트에게 JWT를 반환해야 한다.

  1. String getToken(String code) throws JsonProcessingException {

카카오에서 받은 코드를 컨트롤러가 서비스에 건내주면 이 메서드에서 그 코드를 이용해 카카오 서버에게 토큰을 요청해서 받아온다.
헤더에는 Content-type만 key-value를 넣어 보내고, body에는 map을 만들어 grant_type, client_id(처음에 카카오에서 받은 REST_API_KEY), redirect_uri, code를 넣는다.
Header와 body를 합쳐 httpEntity를 만들어 카카오에 토큰 요청을 보낸다.
카카오에서 받은 JSON 형태의 response를 파싱해서 객체에 담아 Access Token을 String으로 반환.

  1. KakaoUserInfoDto getKakaoUserInfo(String accessToken) throws JsonProcessingException {

위에서 받은 토큰으로 카카오 API를 호출해 카카오에서 사용자 정보를 가져오는 메서드.
헤더에 "Authorization" : "Bearer " + accessToken 을 넣고 Content-type도 넣는다.
그리고 다시 카카오 API에 요청을 보내서 유저 정보를 받아온다.

  1. User registerKakaoUserIfNeeded(KakaoUserInfoDto kakaoUserInfo) {

카카오에서 받아온 유저정보가 현재 우리 서버의 DB에 저장되어있지 않다면 저장을 하는 메서드
먼저, kakaoId로 검색하고 정보가 없으면 email로 한번 더 확인 해본다. email이 있으면 이미 가입한 회원이니 그 유저에 kakaoId를 추가한다. 아예 kakaoId랑 email 정보가 없으면 유저 정보를 새로 저장시킨다. 이때 password는 UUID.randomUUID().toString()으로 랜덤값을 만들어 passwordEncoder로 변환시킨 후 저장한다. 권한은 무조건 USER로. 최종 반환값은 유저로 반환.

  1. public String kakaoLogin(String code, HttpServletResponse response) {

위의 3개의 private 메서드를 순차적으로 실행시켜 code를 통해 User로 반환 받은 상태에서
그 User의 username과 role을 게터로 받아와 jwtUtil.createToken(이름,롤)로 토큰을 생성
생성된 String타입의 토큰을 반환한다.


  • 그러면 Controller에서는 code를 서비스에 보내서 String타입의 JWT 토큰을 받아온 상태이다. 그 토큰을 클라이언트에게 주기 위해 쿠키객체를 새로 만들어 쿠키 자체를 클라이언트에게 보내준다. 이렇게 하면 클라이언트가 쿠키저장소에 넣지 않아도 자동으로 쿠키저장소에 들어간다.
    원래 쓰던 방식은 Service에서 response의 header에 토큰을 담아 줬었는데 두 방법 다 가능하다. 두 방법중 프론트와 협의해서 하나를 골라 사용하면 된다.
profile
꾸준히 성장하기 위해 매일 log를 남깁니다!

0개의 댓글