本文为 social-auth-app-django 使用记录。(官方文档)
0. 前言
前提:有两个网站,记为网站A、网站B,两个网站有各自的用户系统,现在网站B要实现使网站A的用户无需注册直接登录自己的网站,这里就要用到第三方登录。
环境: Django == 3.x??social-auth-app-django == 4.0.0
1. 安装?
pip install social-auth-app-django
2. 使用
网站A 为该篇文章的 provider 项目,即 OAuth provider(提供方);?
创建 Django 项目(网站B),即 OAuth consumer(使用方),网站B目录树如下图:
├─manage.py
├─social
│ │ asgi.py
│ │ settings.py
│ │ urls.py
│ │ wsgi.py
│ └─ __init__.py
└─users
│ admin.py
│ apps.py
│ backends.py
│ models.py
│ tests.py
│ views.py
└─ __init__.py
并进行相关配置:?
# social/settings.py
INSTALLED_APPS = [
...
'users',
'social_django', # add
]
SOCIAL_AUTH_ADMIN_USER_SEARCH_FIELDS = ['username', 'email']
# SOCIAL_AUTH_要使用登录模块的名称大写_KEY
# 登录模块名对应为第三步中自定义后端的 name 属性
SOCIAL_AUTH_DJANGO_KEY = 'local'
SOCIAL_AUTH_DJANGO_SECRET = 'local'
# 登录成功后跳转页面
LOGIN_REDIRECT_URL = '/admin/'
# 获取授权码的标识
PARTIAL_PIPELINE_TOKEN_NAME = 'code'
AUTHENTICATION_BACKENDS = (
'users.backends.djangoOauth2', # 自定义第三方登录认证
'django.contrib.auth.backends.ModelBackend', # django 默认登录认证
)
# 获取用户详细的信息
SOCIAL_AUTH_PIPELINE = (
'social_core.pipeline.social_auth.social_details',
'social_core.pipeline.social_auth.social_uid',
'social_core.pipeline.social_auth.social_user',
'social_core.pipeline.user.get_username',
'social_core.pipeline.user.create_user',
'social_core.pipeline.social_auth.associate_user',
'social_core.pipeline.social_auth.load_extra_data',
'social_core.pipeline.user.user_details',
)
?3.自定义认证后端?
配置文件 settings.py 中的?AUTHENTICATION_BACKENDS?下自定义了一个 djangoOauth2,代码实现如下:
import requests
from social_core.backends.oauth import BaseOAuth2
class djangoOauth2(BaseOAuth2):
name = 'django' # 调用的第三方名称, 如 google
AUTHORIZATION_URL = 'http://127.0.0.1:8000/o/authorize/' # 获取授权码的路由
ACCESS_TOKEN_URL = 'http://127.0.0.1:8000/o/token/' # 获取 token 的路由
ACCESS_TOKEN_METHOD = 'POST' # 获取 token 的请求方式
RESPONSE_TYPE = 'code'
REDIRECT_STATE = False
STATE_PARAMETER = False
EXTRA_DATA = [
('id', 'id'),
('username', 'username'),
]
def get_user_details(self, response):
"""Return user details from Django siteA. """
print('get_user_details')
return response
def user_data(self, access_token, response=None, *args, **kwargs):
"""Return user data"""
# 本处使用了用户id获取用户详细信息
# 由于网站A(provider)项目中返回的 token 信息并不包含 id 信息
# 因此这里网站A 自定义了返回 token信息(代码见下)
url = 'http://127.0.0.1:8000/users/{}'.format(response['id'])
headers = {'Authorization': 'Bearer ' + access_token}
user_data = self.get_json(
url=url,
method='GET',
headers=headers
)
print(user_data)
return user_data
注意:此处为网站A(provider)中自定义的 token 代码:
# provider/users/views.py
import json
from django.http import HttpResponse
from django.utils.decorators import method_decorator
from django.views.decorators.debug import sensitive_post_parameters
from oauth2_provider.models import get_access_token_model
from oauth2_provider.signals import app_authorized
from oauth2_provider.views import TokenView
class CustomTokenView(TokenView):
""" 自定义返回令牌 """
@method_decorator(sensitive_post_parameters("password"))
def post(self, request, *args, **kwargs):
url, headers, body, status = self.create_token_response(request)
if status == 200:
body = json.loads(body)
access_token = body.get("access_token")
if access_token is not None:
token = get_access_token_model().objects.get(token=access_token)
app_authorized.send(sender=self, request=request, token=token)
body['id'] = token.user.id
body = json.dumps(body)
response = HttpResponse(content=body, status=status)
for k, v in headers.items():
response[k] = v
return response
# provider/urls.py
import oauth2_provider.views as oauth2_views
from django.conf import settings
from django.contrib import admin
from django.urls import path, include
from users.utils import CustomTokenView
from users.views import UserList, UserDetails
# OAuth2 provider endpoints
oauth2_endpoint_views = [
path('authorize/', oauth2_views.AuthorizationView.as_view(), name="authorize"),
# 这里使用自定义 token 信息
path('token/', CustomTokenView.as_view(), name="token"),
path('revoke_token/', oauth2_views.RevokeTokenView.as_view(), name="revoke-token"),
]
if settings.DEBUG:
# OAuth2 Application Management endpoints
oauth2_endpoint_views += [
path('applications/', oauth2_views.ApplicationList.as_view(), name="list"),
path('applications/register/', oauth2_views.ApplicationRegistration.as_view(), name="register"),
path('applications/<pk>/', oauth2_views.ApplicationDetail.as_view(), name="detail"),
path('applications/<pk>/delete/', oauth2_views.ApplicationDelete.as_view(), name="delete"),
path('applications/<pk>/update/', oauth2_views.ApplicationUpdate.as_view(), name="update"),
]
# OAuth2 Token Management endpoints
oauth2_endpoint_views += [
path('authorized-tokens/', oauth2_views.AuthorizedTokensListView.as_view(), name="authorized-token-list"),
path('authorized-tokens/<pk>/delete/', oauth2_views.AuthorizedTokenDeleteView.as_view(),
name="authorized-token-delete"),
]
urlpatterns = [
path('admin/', admin.site.urls),
# path('o/', include('oauth2_provider.urls', namespace='oauth2_provider')), # before
path('o/', include((oauth2_endpoint_views, 'oauth2_provider'), namespace="oauth2_provider")), # now
path('users/', UserList.as_view()),
path('users/<pk>/', UserDetails.as_view()),
]
?4. 在网站A中注册网站B
注意:此时网站A在8000端口运行,网站B在8001端口运行。
网站B要想调用网站A登录,则需要在网站A中注册自己网站的信息,注册地址为:http://127.0.0.1:8000/o/applications/, 点击 New Applications 进行添加:
?
其中填写的 client_id?对应配置文件 settings.py 中的 SOCIAL_AUTH_DJANGO_KEY, client_secret 对应?SOCIAL_AUTH_DJANGO_SECRRT。
授权类型为授权码类型(authorization);
重定向地址为 http://127.0.0.1:8001/complete/django/,其中 8001 为网站B运行的端口,django 为自定义认证后端类中的 name。
5. 调用第三方登录
在浏览器中输入 http://127.0.0.1:8001/login/django/,
会发现重定向路由为 http://127.0.0.1:8000/o/authorize/?client_id=local&redirect_uri=http://127.0.0.1:8001/complete/django/&response_type=code,
输入网站A中的用户名及密码,点击授权(Authorize),?
会发现网址重定向到了 http://127.0.0.1:8001/admin/login/?next=/admin/,红字表示已经使用网站A中的 admin 用户登录成功,但是该用户没有访问该页面的权限(superuser才可以访问)。
?同时Pycharm中会输出:
?创建网站B的超级用户(social)并查看 http://127.0.0.1:8001/admin 界面:
|