ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • django kakao login
    Project using python/Cloning Airbnb 2021. 3. 3. 15:54

    url 설정

    config/urls.py

    urlpatterns = [
        path("users", include("users.urls", namespace="users")),
    ]

    users/urls.py

    from django.urls import path
    from . import views
    
    app_name = "users"
    
    urlpatterns = [
        path("login/kakao/", views.kakao_login, name="kakao-login"),
        path(
            "login/kakao/callback/",
            views.kakao_login_callback,
            name="kakao-callback",
        ),
    ]
    

    {% url %}

      url template tag를 이용해서 signup page로 갈 수 있게 한다. {% url 'users:github-login' %}은 /users/login/github와 같다.

    mixins/social_login.html

    Kakao API 설정

    https://developers.kakao.com/ 에서 애플리케이션을 등록 해야한다. '내 애플리케이션'에 들어가자.

    '애플리케이션 추가하기' 선택

    앱 이름과 회사 이름 등록

    REST API키를 복사해서 .env 파일에 KAKAO_ID로 저장한다.

    좌측 메뉴에 카카오 로그인 -> 보안 클릭

      Client Secret 키를 생성해야 한다. 코드 생성 버튼으로 key를 생성하고 .env 파일에 넣자.

    좌측 메뉴에 플랫폼을 클릭한다.

    Web 플랫폼을 선택한다.

    당신의 서버의 IP나 domain 주소를 입력한다.

    좌측 메뉴에 제품 설정에서 카카오 로그인을 클릭한다.

    활성화 설정에서 상태를 ON으로 한다.

    Redirect URI를 등록한다.

    주소/users/login/kakao/callback으로 redirect URI를 설정한다. 마지막에 slash(/)을 꼭 넣어줘야 한다!!!!

    좌측 메뉴에 제품설정 -> 동의 항목을 선택한다.

      자신의 앱에 필요한 정보들을 선택하고 설정한다. login을 위해서는 email이 반드시 필요하니 설정하도록 하자.

    이메일은 사용자에게 값이 없는 경우 카카오 계정 정보 입력을 요청하여 수집하기를 체크한다.

    Coding

    docs를 보면서 kakao login을 만들어 보자.

    1. 인가 코드 받기

      빨간색 박스는 필요한 parameter니 꼭 기입해 주자.

    from django.shortcuts import redirect, reverse
    from django.contrib import messages
    
    def kakao_login(request):
        try:
            if request.user.is_authenticated:
                raise SocialLoginException("User already logged in")
            client_id = os.environ.get("KAKAO_ID")
            redirect_uri = "http://127.0.0.1:8000/users/login/kakao/callback/"
    
            return redirect(
                f"https://kauth.kakao.com/oauth/authorize?client_id={client_id}&redirect_uri={redirect_uri}&response_type=code"
            )
        except KakaoException as error:
            messages.error(request, error)
            return redirect("core:home")
        except SocialLoginException as error:
            messages.error(request, error)
            return redirect("core:home")

    2. 추가 항목 동의 받기

      Kakao에 요청을 하면 내가 지정한 callback 주소로 redirect가 된다. 그렇게 되면 kakao_login_callback 함수가 실행이 되는데, 이 함수는 github가 보낸 code 값을 받게 된다. 이 code 값을 토대로 다시 python의 requests module을 사용해서 parameter와 같이 https://kauth.kakao.com/oauth/token 주소로 요청을 한다. 요청을 할 때 headers는 json을 받을 수 있게 해야 한다.

      그렇게 되면 응답은 json 형식의 access_token을 준다. 받은 엑세스 토큰을 사용해서 kakao의 계정 정보를 추출해 내고 사용자를 저장하고 로그인 시킨다. 이 모든 것을 코드로 나타내면 다음과 같다.

      if user is not None은 kakao로 로그인을 하려는 user의 이메일 계정이 이미 회원가입이 되었는지를 확인한다. 만약 이미 계정이 있다면 해당 계정이 kakao 계정인지 다른 social 계정인지 아니면 airbnb app에 자체 등록한 계정인지 확인한다. kakao 계정이면 바로 로그인을 하고 아니면 회원가입을 한 계정으로 로그인을 하라고 알린다. 만약 계정이 없다면 계정을 생성한다.

      user.avatar.save()는 avatar라는 ImageField(or FileField)의 method로 바로 avatar image를 저장한다. 인자로는 파일 이름과 content 파일을 받는다. content 파일이란 0과 1로 이뤄진 byte 파일이다. django의 ContentFile function을 사용해서 github의 image file을 content file로 변환할 수 있다.

      user.set_unusable_password()는 password 설정을 하지 않는다는 것이다. 왜냐하면 kakao를 이용한 로그인이기 때문이다(kakao의 id와 password를 이용하기 때문이다.).

    def kakao_login_callback(request):
        try:
            if request.user.is_authenticated:
                raise SocialLoginException("User already logged in")
            code = request.GET.get("code", None)
            if code is None:
                KakaoException("Can't get code")
            client_id = os.environ.get("KAKAO_ID")
            redirect_uri = "http://127.0.0.1:8000/users/login/kakao/callback/"
            client_secret = os.environ.get("KAKAO_SECRET")
            request_access_token = requests.post(
                f"https://kauth.kakao.com/oauth/token?grant_type=authorization_code&client_id={client_id}&redirect_uri={redirect_uri}&code={code}&client_secret={client_secret}",
                headers={"Accept": "application/json"},
            )
            access_token_json = request_access_token.json()
            error = access_token_json.get("error", None)
            if error is not None:
                print(error)
                KakaoException("Can't get access token")
            access_token = access_token_json.get("access_token")
            headers = {"Authorization": f"Bearer {access_token}"}
            profile_request = requests.post(
                "https://kapi.kakao.com/v2/user/me",
                headers=headers,
            )
            profile_json = profile_request.json()
            kakao_account = profile_json.get("kakao_account")
            profile = kakao_account.get("profile")
    
            nickname = profile.get("nickname", None)
            avatar_url = profile.get("profile_image_url", None)
            email = kakao_account.get("email", None)
            gender = kakao_account.get("gender", None)
    
            user = models.User.objects.get_or_none(email=email)
            if user is not None:
                if user.login_method != models.User.LOGIN_KAKAO:
                    raise GithubException(f"Please login with {user.login_method}")
            else:
                user = models.User.objects.create_user(
                    email=email,
                    username=email,
                    first_name=nickname,
                    gender=gender,
                    login_method=models.User.LOGIN_KAKAO,
                )
    
                if avatar_url is not None:
                    avatar_request = requests.get(avatar_url)
                    user.avatar.save(
                        f"{nickname}-avatar", ContentFile(avatar_request.content)
                    )
                user.set_unusable_password()
                user.save()
            messages.success(request, f"{user.email} signed up and logged in with Kakao")
            login(request, user)
            return redirect(reverse("core:home"))
        except KakaoException as error:
            messages.error(request, error)
            return redirect(reverse("core:home"))
        except SocialLoginException as error:
            messages.error(request, error)
            return redirect(reverse("core:home"))
    

    Model

    users/models.py

      user model에 새롭게 추가된 field다. social_login을 하게 되면서 local, github와 kakao로 method를 나눴다.

    from django.contrib.auth.models import AbstractUser
    from django.db import models
    
    
    class User(AbstractUser):
    
        """ Custom User model """
    
        LOGIN_EMAIL = "email"
        LOGIN_GITHUB = "github"
        LOGIN_KAKAO = "kakao"
    
        LOGIN_CHOICES = (
            (LOGIN_EMAIL, "Email"),
            (LOGIN_GITHUB, "Github"),
            (LOGIN_KAKAO, "Kakao"),
        )
    
        login_method = models.CharField(
            max_length=6, choices=LOGIN_CHOICES, default=LOGIN_EMAIL
        )

    Exception

    users/exception.py

      여러 가지 exception들을 모아 놓은 파일이다.

    class SocialLoginException(Exception):
        pass
    
    
    class KakaoException(Exception):
        pass
    

    templates

    mixins/social_login.html

      social_login.html이다. mixins에 만들어 놓고 signup과 login에서 include를 한다.

    <div class="social-login mt-4">
      <button class="button bg-gray-700 text-white">
        <a href="{% url 'users:github-login' %}">
          <i class="fab fa-github mr-2"></i>Login with Github
        </a>
      </button>
      <button class="button bg-yellow-300 text-yellow-900 mt-2">
        <a href="{% url 'users:kakao-login' %}">
          <i class="fas fa-comment mr-2"></i>Login with Kakao
        </a>
      </button>
    </div>
    

    login page

    참고 자료

    소스 코드

    github.com/zpskek/airbnb-clone-v3/commit/49420ebc8bfa89cd54cbf9c8862d7ab974e1df60

    'Project using python > Cloning Airbnb' 카테고리의 다른 글

    django user profile FBV  (0) 2021.03.04
    django mailgun.com 연동(interlocking)  (0) 2021.03.04
    django managers.py  (0) 2021.03.03
    django github login  (0) 2021.03.03
    django messages framework  (0) 2021.03.02

    댓글

Designed by Tistory.