2차 수정
1차 수정을 마친 이후 Profile 모델의 실용화를 고민해 봤을 때, 가장 큰 문제는 "개인정보"였다.
따라서 개인정보를 서비스를 제공하는 차원에서 보관하고 있는 것이 아니라, OAuth를 이용하자는 결과가 나왔다.
(OAuth가 뭔지 모른다면, 여기로 가서 2번 강의를 듣기를 추천)
우선, Django에서 제공해 주고 있는 소셜 로그인을 위한 패키지를 설치한다.
pip install social-auth-app-django
설치를 완료했으면, 장고에게 해당 패기지가 설치되었다고 알려야 하니, settings.py 파일에 다음 문장을 추가한다.
INSTALLED_APPS = (
...
'social_django',
...
)
앱 추가 후 migrate를 해줘야 정상적으로 이용이 가능해진다.
python manage.py migrate
Social Login은 기존 유저모델과 함께 사용이 가능하지만, Back-end는 독자적으로 사용하므로, 추가해주어야 한다.
settings.py 파일에 아래 항목을 추가시키자.
AUTHENTICATION_BACKENDS = [
'social_core.backends.google.GoogleOAuth2', # Google
'django.contrib.auth.backends.ModelBackend', # Django 기본 유저모델
]
트위터면 'social_core.backends.twitter.TwitterOAuth'
, 페이스북이면 'social_core.backends.facebook.FacebookOAuth2'
등 여러 가지 각각의 Back-end가 있다.
이제 구글하고 연동을 시키는 본격적인 작업에 들어가보자.
urls.py
from django.conf.urls import url, include
from django.contrib import admin
from mysite.views import home
urlpatterns = [
url(r'^admin/', admin.site.urls),
url(r'^$', home.as_view(), name='home'),
url(r'^oauth/', include('social_django.urls', namespace='social')), # <--
]
위 코드에서 맨 아래 있는 녀석이 구글과 연동되는 부분이다.
아, 물론 구글에서 하는 일도 "로그인"이기 때문에 settings.py 파일에 LOGIN_REQUIRED_URL = "myapp"
을 추가시켜줘야 한다.
그리고, social이라는 namespace를 사용한다는 것도 알려야해서, SOCIAL_AUTH_URL_NAMESPACE = 'social'
도 추가하자.
views.py
from django.views.generic import RedirectView
class home(RedirectView):
url = "/oauth/login/google-oauth2/"
뷰에서는 그냥 바로 저기로 redirect 시켜줬다. (왜 url이 저모양인지는 묻지 말자. social-auth-app-django님의 내부에서 처리해주시는 거니까. 지금만 바로 url을 보여주는 것 뿐)
저렇게 url을 넘겨주면 이후에는 알아서 처리된다. 로그인이 끝나면 settings.py 파일에 저장된 LOGIN_REQUIRED___URL로 이동된다.
지금까지의 과정중에서 google api 관리자에서 다뤄야 하는 부분은 생략했다.
실제로 실행해보면 한가지 오류가 뜬다.
지금 Profile은 User를 1:1로 연결한 상태가 아니라, AbstracUser를 상속받아 자기가 User모델이 되었기 때문에 조금의 처리를 해야한다.
student_id, name, nickname 항목이 not null인데 빈칸으로 저장하려 한다고 노발대발 한다.
이를 처리해 주기 위해서 social login의 로직을 알아야 할 필요성이 생겼다.
social login은 pipeline이라는 것을 통해 어떻게 실행되는지 차례로 알려주는데, 이는 settings.py 파일에서 선언한다.
SOCIAL_AUTH_PIPELINE = (
'social.pipeline.social_auth.social_details',
'social.pipeline.social_auth.social_uid',
'social.pipeline.social_auth.auth_allowed',
'social.pipeline.social_auth.social_user',
'social.pipeline.user.get_username',
'social.pipeline.user.create_user',
'social.pipeline.social_auth.associate_user',
'social.pipeline.social_auth.load_extra_data',
'social.pipeline.user.user_details'
)
아, 이걸 실행하기 위해서는 아래 명령어를 실행해줘야 한다. social이라는 객체가 따로 있는 모양이다.
pip install python-social-auth
하여튼, 저 위의 pipeline이라는 것을 하나씩하나씩 차례대로 실행한다.
자세히 보면, 중간에 get_username, create_user가 있는데, 아마 create_user에서 문제가 발생했겠지.
그래서 create_user를 재정의 해줬다. (social.py라는 파일을 만들었다.)
social.py
from django.shortcuts import render_to_response
def customize_user_data(strategy, details, response, *args, **kwargs):
if kwargs['backend'].name == 'google-oauth2':
if details['first_name'] != "학부생":
return render_to_response('login_failed.html', {'error': "학부생이 아닙니다."})
elif response['domain'] != "handong.edu":
return render_to_response('login_failed.html', {'error': "handong.edu 아이디만 접속할 수 있습니다."})
return None
def create_user(strategy, details, user=None, *args, **kwargs):
if user:
return {'is_new': False}
fields = {'username': details.get('email'), 'name': details.get('last_name'), 'nickname': details.get('last_name'), 'student_id': details.get('username')}
if not fields:
return
return {
'is_new': True,
'user': strategy.create_user(**fields)
}
위에 있는 함수는 단순히 한동대학교 학생인가를 감지하는 함수고, 아래있는 함수가 저장하는 함수이다.
어떻게 자세한 설명은 해줄수가 없어서 그냥 잘 보세요들.
(print(kwargs)와 같은 함수 넣어주면 어떤 값이 넘어오는지 로그로 볼 수 있어요)
그래서 위에서 보여준 pipeline을 아래처럼 조금 바꿨다.
SOCIAL_AUTH_PIPELINE = (
'social.pipeline.social_auth.social_details',
'social.pipeline.social_auth.social_uid',
'social.pipeline.social_auth.auth_allowed',
'social.pipeline.social_auth.social_user',
'social.pipeline.user.get_username',
'base.social.customize_user_data', # <--
'base.social.create_user', # <--
'social.pipeline.social_auth.associate_user',
'social.pipeline.social_auth.load_extra_data',
'social.pipeline.user.user_details'
)
결론. 잘된다.
만약에 social login이 안된다면 아래를 시도해보면 될 수도 있어요.
구글링은 다 이거 하라고 하는거같던데, 이거 빼도 작동하길래...
미들웨어에 소셜 로그인을 추가해준다. (왜 하는지는 묻지 말자.)
MIDDLEWARE_CLASSES = [
...
'social_django.middleware.SocialAuthExceptionMiddleware',
...
]
템플릿에 소셜로그인을 추가해준다.
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [
PROJECT_DIR.child('templates'),
],
'APP_DIRS': True,
'OPTIONS': {
'context_processors': [
'django.template.context_processors.debug',
'django.template.context_processors.request',
'django.contrib.auth.context_processors.auth',
'django.contrib.messages.context_processors.messages',
'social_django.context_processors.backends', # <--
'social_django.context_processors.login_redirect', # <--
]
},
},
]