Django之第三方平臺QQ授權(quán)登錄的實現(xiàn)
環(huán)境準(zhǔn)備
創(chuàng)建QQ互聯(lián)應(yīng)用
創(chuàng)建一個QQ互聯(lián)應(yīng)用,并獲取到App ID和App Key。
QQ互聯(lián)官網(wǎng):https://connect.qq.com/
開發(fā)文檔:https://wiki.connect.qq.com/
創(chuàng)建應(yīng)用模塊
創(chuàng)建一個新的應(yīng)用oauth,用來實現(xiàn)QQ第三方認(rèn)證登錄的代碼編寫。
python manage.py startapp oauth
在settings.py中注冊應(yīng)用
INSTALLED_APPS = [
# 'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'apps.user',
'apps.oauth',
]在settings.py同級目錄下的urls.py設(shè)置路由
url(r'^', include('apps.oauth.urls',namespace='oauth')),定義QQ登錄模型類
在oauth/models.py中定義QQ身份(openid)與用戶模型類User的關(guān)聯(lián)關(guān)系
from django.db import models
class OAuthQQUser():
"""QQ登錄用戶"""
user = models.ForeignKey('user.User', on_delete=models.CASCADE, verbose_name='用戶')
openid = models.CharField(max_length=64, verbose_name='openid', db_index=True)
create_time = models.DateTimeField(auto_now_add=True, verbose_name="創(chuàng)建時間")
update_time = models.DateTimeField(auto_now=True, verbose_name="更新時間")
class Meta:
db_table = 'tb_oauth_qq'
verbose_name = 'QQ登錄用戶'
verbose_name_plural = verbose_name執(zhí)行遷移
執(zhí)行遷移操作,生成QQ登錄模型類對應(yīng)的數(shù)據(jù)庫表
python manage.py makemigrations python manage.py migrate
QQLoginTool庫
騰訊QQ互聯(lián)平臺沒有Python SDK,但是可以使用第三方封裝好的SDK包,QQLoginTool是一個第三方庫,封裝了對接QQ互聯(lián)的請求操作,可用于快速實現(xiàn)QQ登錄的一種工具包。
安裝QQLoginTool
pip install QQLoginTool
API使用說明
導(dǎo)入
from QQLoginTool.QQtool import OAuthQQ
初始化OAuthQQ對象
oauth = OAuthQQ(client_id=settings.QQ_CLIENT_ID, client_secret=settings.QQ_CLIENT_SECRET, redirect_uri=settings.QQ_REDIRECT_URI, state=next)
獲取QQ登錄掃碼頁面,掃碼后得到Authorization Code
login_url = oauth.get_qq_url()
通過Authorization Code獲取Access Token
access_token = oauth.get_access_token(code)
通過Access Token獲取OpenID
openid = oauth.get_open_id(access_token)
在settings.py配置QQ登錄參數(shù)
QQ_CLIENT_ID = '1020343878' QQ_CLIENT_SECRET = 'Yu4123456LG0Yw53o' QQ_REDIRECT_URI = 'https://abc.com/oauth/callback'
QQ登錄掃碼頁面
from django.urls import re_path
from . import views
urlpatterns = [
re_path(r'^qq/login/$', views.QQAuthURLView.as_view()),
]from QQLoginTool.QQtool import OAuthQQ
from django import http
from django.conf import settings
from django.views import View
from utils.response_code import RETCODE
"""
提供QQ登錄頁面網(wǎng)址
https://graph.qq.com/oauth2.0/authorize?response_type=code&client_id=xxx&redirect_uri=xxx&state=xxx
"""
class QQAuthURLView(View):
def get(self, request):
# next: 從哪個頁面進(jìn)入到的登錄頁面,登錄成功后自動回到那個頁面
next = request.GET.get('next')
# 獲取QQ登錄頁面網(wǎng)址
oauth = OAuthQQ(client_id=settings.QQ_CLIENT_ID, client_secret=settings.QQ_CLIENT_SECRET,
redirect_uri=settings.QQ_REDIRECT_URI, state=next)
login_url = oauth.get_qq_url()
return http.JsonResponse({'code': 200 , 'msg': 'OK', 'login_url': login_url})認(rèn)證獲取openid
1.用戶在QQ登錄成功后,QQ會將用戶重定向到配置的回調(diào)網(wǎng)址,同時會傳遞一個Authorization Code 2.拿到Authorization Code并完成OAuth2.0認(rèn)證獲取openid
注意:回調(diào)網(wǎng)址在申請QQ登錄開發(fā)資質(zhì)時進(jìn)行配置
from django.urls import re_path
from . import views
urlpatterns = [
re_path(r'^oauth/callback/$', views.QQAuthUserView.as_view()),
]使用code向QQ服務(wù)器請求,獲取access_token
使用access_token向QQ服務(wù)器請求獲取openid
"""用戶掃碼登錄的回調(diào)處理"""
class QQAuthUserView(View):
def get(self, request):
"""Oauth2.0認(rèn)證"""
# 提取code請求參數(shù)
code = request.GET.get('code')
if not code:
return http.HttpResponseBadRequest('缺少code')
# 創(chuàng)建oauth 對象
oauth = OAuthQQ(client_id=settings.QQ_CLIENT_ID, client_secret=settings.QQ_CLIENT_SECRET,
redirect_uri=settings.QQ_REDIRECT_URI)
try:
# 使用code向QQ服務(wù)器請求access_token
access_token = oauth.get_access_token(code)
# 使用access_token向QQ服務(wù)器請求openid
openid = oauth.get_open_id(access_token)
except Exception as e:
logger.error(e)
return http.HttpResponseServerError('OAuth2.0認(rèn)證失敗')
passopenid的判斷處理
openid是否綁定過用戶
判斷openid是否綁定過用戶,只需要使用openid查詢該QQ用戶是否綁定過用戶即可。
oauth_user = OAuthQQUser.objects.get(openid=openid)
openid已綁定用戶
如果openid已綁定用戶,直接生成狀態(tài)保持信息,登錄成功,并重定向到首頁。
try:
oauth_user = OAuthQQUser.objects.get(openid=openid)
except OAuthQQUser.DoesNotExist:
# 如果openid沒綁定用戶
pass
else:
# 如果openid已綁定用戶,實現(xiàn)狀態(tài)保持
qq_user = oauth_user.user
login(request, qq_user)
# 響應(yīng)結(jié)果
next = request.GET.get('state')
response = redirect(next)
# 登錄時用戶名寫入到cookie,有效期15天
response.set_cookie('username', qq_user.username, max_age=3600 * 24 * 15)
return responseopenid未綁定用戶
openid屬于用戶隱私信息,在后續(xù)的綁定用戶操作中前端會使用openid,因此需要將openid簽名處理,避免暴露。
try:
oauth_user = OAuthQQUser.objects.get(openid=openid)
except OAuthQQUser.DoesNotExist:
# 如果openid沒綁定用戶 generate_eccess_token:對openid簽名
access_token = generate_eccess_token(openid)
context = {'access_token': access_token}
return render(request, 'oauthCallback.html', context)
else:
qq_user = oauth_user.user
login(request, qq_user)
response = redirect(reverse('contents:index'))
response.set_cookie('username', qq_user.username, max_age=3600 * 24 * 15)
return responseoauthCallback.html中渲染access_token
<input type="hidden" name="access_token" value="{{ access_token }}">openid簽名處理
簽名處理可以使用itsdangerous庫,它是一個用于Python語言的庫,提供了一些安全傳輸數(shù)據(jù)的工具類。其主要功能是在保證數(shù)據(jù)安全性的前提下,生成認(rèn)證令牌、時間限制的令牌和加密/解密數(shù)據(jù)信息等。
安裝itsdangerous
pip install itsdangerous
使用TimedJSONWebSignatureSerializer可以生成帶有有效期的token
from itsdangerous import TimedJSONWebSignatureSerializer as Serializer
from django.conf import settings
# serializer = Serializer(秘鑰, 有效期秒)
serializer = Serializer(settings.SECRET_KEY, 300)
# serializer.dumps(數(shù)據(jù)), 返回bytes類型
token = serializer.dumps({'mobile': '18381234567'})
token = token.decode()
# 檢驗token
# 驗證失敗,會拋出itsdangerous.BadData異常
serializer = Serializer(settings.SECRET_KEY, 300)
try:
data = serializer.loads(token)
except BadData:
return None生成openid簽名與校驗
from itsdangerous import BadData
from itsdangerous import TimedJSONWebSignatureSerializer as Serializer
from django.conf import settings
def generate_eccess_token(openid):
"""
對openid簽名
:param openid: 用戶openid
"""
serializer = Serializer(settings.SECRET_KEY, expires_in=3600)
data = {'openid': openid}
token = serializer.dumps(data)
return token.decode()
def check_access_token(access_token):
"""
提取openid
:param access_token: 簽名后的openid
"""
serializer = Serializer(settings.SECRET_KEY, expires_in=3600)
try:
data = serializer.loads(access_token)
except BadData:
return None
else:
return data.get('openid')openid綁定用戶
openid綁定用戶的過程類似于用戶注冊的業(yè)務(wù)邏輯
class QQAuthUserView(View):
"""用戶掃碼登錄的回調(diào)處理"""
def get(self, request):
"""Oauth2.0認(rèn)證"""
......
def post(self, request):
"""用戶綁定openid"""
# 接收參數(shù)
mobile = request.POST.get('mobile')
password= request.POST.get('password')
sms_code_client = request.POST.get('sms_code')
access_token = request.POST.get('access_token')
# 判斷參數(shù)是否齊全
if not all([mobile, password, sms_code_client]):
return http.HttpResponseBadRequest('缺少必傳參數(shù)')
# 判斷手機(jī)號是否合法
if not re.match(r'^1[3-9]\d{9}$', mobile):
return http.HttpResponseBadRequest('請輸入正確的手機(jī)號碼')
# 判斷密碼是否合格
if not re.match(r'^[0-9A-Za-z]{8,20}$', password):
return http.HttpResponseBadRequest('請輸入8-20位的密碼')
# 判斷短信驗證碼是否一致
redis_conn = get_redis_connection('code')
sms_code_server = redis_conn.get('sms_%s' % mobile)
if sms_code_server is None:
return render(request, 'oauthCallback.html', {'msg': '無效的短信驗證碼'})
if sms_code_client != sms_code_server.decode():
return render(request, 'oauthCallback.html', {'msg': '輸入短信驗證碼有誤'})
# 判斷openid是否有效
openid = check_access_token(access_token)
if not openid:
return render(request, 'oauthCallback.html', {'msg': '無效的openid'})
# 保存注冊數(shù)據(jù)
try:
user = User.objects.get(mobile=mobile)
except User.DoesNotExist:
# 用戶不存在,新建用戶
user = User.objects.create_user(username=mobile, password=password, mobile=mobile)
else:
# 如果用戶存在,檢查用戶密碼
if not user.check_password(password):
return render(request, 'oauthCallback.html', {'msg': '用戶名或密碼錯誤'})
# 將用戶綁定openid
try:
OAuthQQUser.objects.create(openid=openid, user=user)
except DatabaseError:
return render(request, 'oauthCallback.html', {'msg': 'QQ登錄失敗'})
# 實現(xiàn)狀態(tài)保持
login(request, user)
# 響應(yīng)綁定結(jié)果
next = request.GET.get('state')
response = redirect(next)
# 登錄時用戶名寫入到cookie,有效期15天
response.set_cookie('username', user.username, max_age=3600 * 24 * 15)
return response到此這篇關(guān)于Django之第三方平臺QQ授權(quán)登錄的實現(xiàn)的文章就介紹到這了,更多相關(guān)Django 第三方平臺QQ授權(quán)登錄內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
高質(zhì)量Python代碼編寫的5個優(yōu)化技巧
這篇文章主要為大家詳細(xì)介紹了編寫高質(zhì)量Python代碼的5個優(yōu)化技巧,具有一定的參考價值,感興趣的小伙伴們可以參考一下2017-11-11
在Pandas中DataFrame數(shù)據(jù)合并,連接(concat,merge,join)的實例
今天小編就為大家分享一篇在Pandas中DataFrame數(shù)據(jù)合并,連接(concat,merge,join)的實例,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2019-01-01
Python數(shù)據(jù)結(jié)構(gòu)與算法之圖的最短路徑(Dijkstra算法)完整實例
這篇文章主要介紹了Python數(shù)據(jù)結(jié)構(gòu)與算法之圖的最短路徑(Dijkstra算法),結(jié)合完整實例形式分析了Python圖的最短路徑算法相關(guān)原理與實現(xiàn)技巧,需要的朋友可以參考下2017-12-12
Python PyQt5模塊實現(xiàn)窗口GUI界面代碼實例
這篇文章主要介紹了Python PyQt5模塊實現(xiàn)窗口GUI界面代碼實例,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下2020-05-05

