欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Django之第三方平臺(tái)QQ授權(quán)登錄的實(shí)現(xiàn)

 更新時(shí)間:2023年05月08日 10:18:12   作者:CodeDevMaster  
本文主要介紹了Django之第三方平臺(tái)QQ授權(quán)登錄的實(shí)現(xiàn),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧

環(huán)境準(zhǔn)備

創(chuàng)建QQ互聯(lián)應(yīng)用

創(chuàng)建一個(gè)QQ互聯(lián)應(yīng)用,并獲取到App ID和App Key。

QQ互聯(lián)官網(wǎng):https://connect.qq.com/

開(kāi)發(fā)文檔:https://wiki.connect.qq.com/

創(chuàng)建應(yīng)用模塊

創(chuàng)建一個(gè)新的應(yīng)用oauth,用來(lái)實(shí)現(xiàn)QQ第三方認(rèn)證登錄的代碼編寫(xiě)。

python manage.py startapp oauth

在settings.py中注冊(cè)應(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同級(jí)目錄下的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)建時(shí)間")
    update_time = models.DateTimeField(auto_now=True, verbose_name="更新時(shí)間")
    class Meta:
        db_table = 'tb_oauth_qq'
        verbose_name = 'QQ登錄用戶'
        verbose_name_plural = verbose_name

執(zhí)行遷移

執(zhí)行遷移操作,生成QQ登錄模型類對(duì)應(yīng)的數(shù)據(jù)庫(kù)表

python manage.py makemigrations
python manage.py migrate

QQLoginTool庫(kù)

騰訊QQ互聯(lián)平臺(tái)沒(méi)有Python SDK,但是可以使用第三方封裝好的SDK包,QQLoginTool是一個(gè)第三方庫(kù),封裝了對(duì)接QQ互聯(lián)的請(qǐng)求操作,可用于快速實(shí)現(xiàn)QQ登錄的一種工具包。

安裝QQLoginTool

pip install QQLoginTool

API使用說(shuō)明

導(dǎo)入

from QQLoginTool.QQtool import OAuthQQ

初始化OAuthQQ對(duì)象

oauth = OAuthQQ(client_id=settings.QQ_CLIENT_ID, client_secret=settings.QQ_CLIENT_SECRET, redirect_uri=settings.QQ_REDIRECT_URI, state=next)

獲取QQ登錄掃碼頁(yè)面,掃碼后得到Authorization Code

login_url = oauth.get_qq_url()

通過(guò)Authorization Code獲取Access Token

access_token = oauth.get_access_token(code)

通過(guò)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登錄掃碼頁(yè)面

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登錄頁(yè)面網(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: 從哪個(gè)頁(yè)面進(jìn)入到的登錄頁(yè)面,登錄成功后自動(dòng)回到那個(gè)頁(yè)面
        next = request.GET.get('next')
        # 獲取QQ登錄頁(yè)面網(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會(huì)將用戶重定向到配置的回調(diào)網(wǎng)址,同時(shí)會(huì)傳遞一個(gè)Authorization Code

2.拿到Authorization Code并完成OAuth2.0認(rèn)證獲取openid

注意:回調(diào)網(wǎng)址在申請(qǐng)QQ登錄開(kāi)發(fā)資質(zhì)時(shí)進(jìn)行配置

from django.urls import re_path
from . import views
urlpatterns = [
    re_path(r'^oauth/callback/$', views.QQAuthUserView.as_view()),
]

使用code向QQ服務(wù)器請(qǐng)求,獲取access_token

使用access_token向QQ服務(wù)器請(qǐng)求獲取openid

"""用戶掃碼登錄的回調(diào)處理"""
class QQAuthUserView(View):
    def get(self, request):
        """Oauth2.0認(rèn)證"""
        # 提取code請(qǐng)求參數(shù)
        code = request.GET.get('code')
        if not code:
            return http.HttpResponseBadRequest('缺少code')
        # 創(chuàng)建oauth 對(duì)象
        oauth = OAuthQQ(client_id=settings.QQ_CLIENT_ID, client_secret=settings.QQ_CLIENT_SECRET,
                        redirect_uri=settings.QQ_REDIRECT_URI)
        try:
            # 使用code向QQ服務(wù)器請(qǐng)求access_token
            access_token = oauth.get_access_token(code)
            # 使用access_token向QQ服務(wù)器請(qǐng)求openid
            openid = oauth.get_open_id(access_token)
        except Exception as e:
            logger.error(e)
            return http.HttpResponseServerError('OAuth2.0認(rèn)證失敗')
        pass

openid的判斷處理

openid是否綁定過(guò)用戶

判斷openid是否綁定過(guò)用戶,只需要使用openid查詢?cè)換Q用戶是否綁定過(guò)用戶即可。

oauth_user = OAuthQQUser.objects.get(openid=openid)

openid已綁定用戶

如果openid已綁定用戶,直接生成狀態(tài)保持信息,登錄成功,并重定向到首頁(yè)。

try:
    oauth_user = OAuthQQUser.objects.get(openid=openid)
except OAuthQQUser.DoesNotExist:
    # 如果openid沒(méi)綁定用戶
    pass
else:
    # 如果openid已綁定用戶,實(shí)現(xiàn)狀態(tài)保持
    qq_user = oauth_user.user
    login(request, qq_user)
    # 響應(yīng)結(jié)果
    next = request.GET.get('state')
    response = redirect(next)
    # 登錄時(shí)用戶名寫(xiě)入到cookie,有效期15天
    response.set_cookie('username', qq_user.username, max_age=3600 * 24 * 15)
    return response

openid未綁定用戶

openid屬于用戶隱私信息,在后續(xù)的綁定用戶操作中前端會(huì)使用openid,因此需要將openid簽名處理,避免暴露。

try:
    oauth_user = OAuthQQUser.objects.get(openid=openid)
except OAuthQQUser.DoesNotExist:
    # 如果openid沒(méi)綁定用戶 generate_eccess_token:對(duì)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 response

oauthCallback.html中渲染access_token

<input type="hidden" name="access_token" value="{{ access_token }}">

openid簽名處理

簽名處理可以使用itsdangerous庫(kù),它是一個(gè)用于Python語(yǔ)言的庫(kù),提供了一些安全傳輸數(shù)據(jù)的工具類。其主要功能是在保證數(shù)據(jù)安全性的前提下,生成認(rèn)證令牌、時(shí)間限制的令牌和加密/解密數(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()
# 檢驗(yàn)token
# 驗(yàn)證失敗,會(huì)拋出itsdangerous.BadData異常
serializer = Serializer(settings.SECRET_KEY, 300)
try:
    data = serializer.loads(token)
except BadData:
    return None

生成openid簽名與校驗(yàn)

from itsdangerous import BadData
from itsdangerous import TimedJSONWebSignatureSerializer as Serializer
from django.conf import settings
def generate_eccess_token(openid):
    """
    對(duì)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綁定用戶的過(guò)程類似于用戶注冊(cè)的業(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ī)號(hào)是否合法
        if not re.match(r'^1[3-9]\d{9}$', mobile):
            return http.HttpResponseBadRequest('請(qǐng)輸入正確的手機(jī)號(hào)碼')
        # 判斷密碼是否合格
        if not re.match(r'^[0-9A-Za-z]{8,20}$', password):
            return http.HttpResponseBadRequest('請(qǐng)輸入8-20位的密碼')
        # 判斷短信驗(yàn)證碼是否一致
        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': '無(wú)效的短信驗(yàn)證碼'})
        if sms_code_client != sms_code_server.decode():
            return render(request, 'oauthCallback.html', {'msg': '輸入短信驗(yàn)證碼有誤'})
        # 判斷openid是否有效
        openid = check_access_token(access_token)
        if not openid:
            return render(request, 'oauthCallback.html', {'msg': '無(wú)效的openid'})
        # 保存注冊(cè)數(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': '用戶名或密碼錯(cuò)誤'})
        # 將用戶綁定openid
        try:
            OAuthQQUser.objects.create(openid=openid, user=user)
        except DatabaseError:
            return render(request, 'oauthCallback.html', {'msg': 'QQ登錄失敗'})
        # 實(shí)現(xiàn)狀態(tài)保持
        login(request, user)
        # 響應(yīng)綁定結(jié)果
        next = request.GET.get('state')
        response = redirect(next)
        # 登錄時(shí)用戶名寫(xiě)入到cookie,有效期15天
        response.set_cookie('username', user.username, max_age=3600 * 24 * 15)
        return response

到此這篇關(guān)于Django之第三方平臺(tái)QQ授權(quán)登錄的實(shí)現(xiàn)的文章就介紹到這了,更多相關(guān)Django 第三方平臺(tái)QQ授權(quán)登錄內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

最新評(píng)論