【Django】Django2實現QQ第三方賬號登錄(十二)


這一篇教程,我們一起使用Python3.6與Django2實現QQ第三方賬號登錄。

首先,如果想使用QQ第三方登錄功能,需要先在QQ互聯(https://connect.qq.com/)進行開發者認證。

認證審覈通過後,創建一個新的網站應用,並提交審覈。

網站應用審覈通過後,我們能夠的到應用的APP ID和APP Key。

另外,網站應用的回調地址建議先修改爲:http://127.0.0.1:8888/login,以方便我們編程過程中進行調試。

有了網站應用的APP ID、APP Key以及回調地址,我們就可以開始編寫代碼了。

在調用QQ的第三方登錄之前,我們先做一些準備工作。

一、創建一個Django項目,並創建應用(例如:website)。

二、進行項目設置。

示例代碼:(settings.py)

INSTALLED_APPS = [
    ...省略部分代碼...
    'website',
]
DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.mysql',
        'NAME': 'website',
        'USER':'root',
        'PASSWORD':'Opython.com666',
        'HOST':'127.0.0.1',
        'PORT':'3306',
    }
}

STATIC_ROOT=os.path.join(BASE_DIR, 'static')

三、創建數據庫與數據模型,通過模型生成數據表。

示例代碼:(models.py)

from django.db import models

class User(models.Model):
    nickname = models.CharField('暱稱', max_length=150)
    openid = models.CharField('ID', max_length=128, primary_key=True)
    head = models.URLField('頭像')
    gender = models.CharField('性別', max_length=2, default='保密')

完成模型類的創建後,執行“makemigrations”和“migrate”命令進行數據表的創建。

如果執行命令時發生錯誤,可以嘗試先通過MySQL命令創建數據庫,命令中聲明字符集爲“utf8”。

mysql>CREATE DATABASE website DEFAULT CHARACTER SET utf8 COLLATE utf8_general_ci;

然後,再重新執行“makemigrations”和“migrate”命令。

四、添加QQ登錄圖標素材。

在應用目錄“website”下創建新的文件夾“static”,並在“static”文件夾下創建文件夾“images”,將QQ登錄的圖標存放在“images”文件夾中。

五、創建登錄頁面模板文件。

示例代碼:(login.html)

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>登錄</title>
</head>
<body>
{% load static %} <!--加載靜態文件目錄-->
{% if userinfo %} <!--如果存在用戶信息-->
    <img src="{{ userinfo.head }}">{{ userinfo.nickname }} <!--顯示頭像與暱稱-->
{% else %} <!--否則-->
    <a href="to_login/"><img src="{% static '/images/qq.png' %}"></a>  <!--顯示登錄圖標-->
{% endif %}
</body>
</html>

上述代碼中,如果登錄之後(存在用戶信息),顯示用戶頭像和暱稱;否則(不存在用戶信息),顯示登錄圖標,可以點擊進行登錄。

六、創建視圖函數。

示例代碼:(views.py)

from django.shortcuts import render
from .models import User

def index(request):
    try:
        openid = request.session['openid']  # 讀取Session
        userinfo = User.objects.get(openid=openid)  # 根據Session獲取用戶信息
        return render(request, 'index.html', {'userinfo': userinfo})
    except:  # 如果發生異常
        return render(request, 'index.html')

七、配置URL分發

示例代碼:(urls.py)

from django.contrib import admin
from django.urls import path
from website import views as site_view

urlpatterns = [
    path('',site_view.index),
    path('admin/', admin.site.urls),
]

完成以上準備工作之後,運行開發服務器即能夠進行登錄頁面的訪問。

接下來,我們完成QQ第三方登錄的關鍵代碼。

QQ第三方登錄需要經過以下過程:

  • 網站應用獲取用戶授權碼(打開登錄頁面)
  • 通過授權碼獲取訪問令牌(Access Token)
  • 通過訪問令牌獲取QQ用戶的openid
  • 通過openid和訪問令牌獲取用戶信息

1、網站應用獲取用戶授權碼

在模板“index.html”中我們爲QQ登錄圖片添加了鏈接“to_login/”,我們在視圖中編寫這個鏈接對應的函數。

示例代碼:(views.py)

from django.shortcuts import HttpResponseRedirect
from urllib import parse
import random
def to_login(request):
    state = str(random.randrange(100000, 999999))  # 定義一個隨機狀態碼,防止跨域僞造攻擊。
    request.session['state'] = state  # 將隨機狀態碼存入Session,用於授權信息返回時驗證。
    client_id = '1*******9'  # QQ互聯中網站應用的APP ID。
    callback = parse.urlencode({'redirect_uri': 'http://127.0.0.1:8888/login'})  
    # 對回調地址進行編碼,用戶同意授權後將調用此鏈接。
    login_url = 'https://graph.qq.com/oauth2.0/authorize?response_type=code&client_id=%s&%s&state=%s' % (
        client_id, callback, state)  # 組織QQ第三方登錄鏈接
    return HttpResponseRedirect(login_url)  # 重定向到QQ第三方登錄授權頁面

示例代碼:(urls.py)

path('to_login/', site_view.to_login),

2、通過授權碼獲取訪問令牌(Access Token)

當用戶在QQ登錄界面中同意授權後,此時會打開回調地址並帶有授權碼的參數“code”。

此時瀏覽器地址欄顯示類似“http://127.0.0.1:8888/login/?code=C84FEE1CBE828DE5CA8BEF973E1E0FE0&state=613473”的地址。

我們需要一個視圖函數對“login/”這個URL進行處理,通過參數“code”獲取訪問令牌。

示例代碼:(urls.py)

path('login/', site_view.login),

示例代碼:(views.py)

from django.shortcuts import HttpResponse
from urllib import request as req
import re
import json
def login(request):
    if request.session['state'] == request.GET['state']:  # 驗證狀態碼,防止跨域僞造攻擊。
        code = request.GET['code']  # 獲取用戶授權碼
        client_id = '1*******9'  # QQ互聯中網站應用的APP ID。
        client_secret = '83b76c870************9ec664b8891'  # QQ互聯中網站應用的APP Key。
        callback = parse.urlencode({'redirect_uri': 'http://127.0.0.1:8888/login'})
        # 對回調地址進行編碼,用戶同意授權後將調用此鏈接。
        login_url = 'https://graph.qq.com/oauth2.0/token?grant_type=authorization_code&code=%s&client_id=%s&client_secret=%s&%s' % (
            code, client_id, client_secret, callback)  # 組織獲取訪問令牌的鏈接
        response = req.urlopen(login_url).read().decode()  # 打開獲取訪問令牌的鏈接
        ...接下一段代碼...

打開獲取訪問令牌的鏈接之後,獲取到的返回數據類似:

access_token=28BEF57C************622BC866E90&expires_in=7*****0&refresh_token=4D24F259****************AD146768

3、通過訪問令牌獲取QQ用戶的openid

在上一步的返回數據中,我們能看到第一部分就是訪問令牌,我們可以提取這個令牌內容,作爲獲取QQ用戶openid的參數。

示例代碼:(接上一段代碼)

access_token = re.split('&', response)[0]  # 獲取訪問令牌
res = req.urlopen('https://graph.qq.com/oauth2.0/me?' + access_token).read().decode()  # 打開獲取openid的鏈接
...接下一段代碼...

打開獲取openid的鏈接之後,獲取到的返回數據類似:

callback( {“client_id”:”1*******9″,”openid”:”0D2DC10E****************66E1F801″} );

爲了獲取返回數據中的openid,我們可以單獨寫一個函數進行解析。

def parse_jsonp(jsonp_str):
    try:
        return re.search('^[^(]*?\((.*)\)[^)]*$', jsonp_str).group(1)
    except:
        raise ValueError('無效數據!')

4、通過openid和訪問令牌獲取用戶信息

此時,我們已經獲取了訪問令牌和openid,就能夠進行用戶信息的獲取了。

示例代碼:(接上一段代碼)

    openid = json.loads(parse_jsonp(res))['openid']  # 從返回數據中獲取openid
    userinfo = req.urlopen('https://graph.qq.com/user/get_user_info?oauth_consumer_key=%s&openid=%s&%s' % (
        client_id, openid, access_token)).read().decode()  # 打開獲取用戶信息的鏈接
    userinfo = json.loads(userinfo)  # 將返回的用戶信息數據(JSON格式)讀取爲字典。
    user = User.objects.get(openid=openid)  # 查詢是否已存在用戶
    if not user:  # 如果不存在用戶
        user = User()  # 創建新用戶
        user.openid = openid  # 寫入用戶信息
    user.nickname = userinfo['nickname']  # 寫入用戶信息
    user.gender = userinfo['gender']  # 寫入用戶信息
    user.head = userinfo['figureurl_qq_1']  # 寫入用戶信息
    user.save()  # 保存或更新用戶
    request.session['openid'] = openid  # 將已登錄的用戶openid寫入Session
    return render(request, 'index.html', {'userinfo': user})
else:
    return HttpResponse('授權失敗!')

注意:獲取的用戶信息爲JSON格式,包含了很多用戶信息內容,可以讀取爲字典然後獲取相關信息。

關於能夠獲取到的用戶信息,可以參考:http://wiki.connect.qq.com/get_user_info

另外,本教程中未對可能出現的異常進行處理(例如獲取用戶信息失敗,可以根據返回的錯誤碼進行處理),僅做正常登錄過程參考。

 

 

轉載請註明:魔力Python » Python3.6與Django2實現QQ第三方賬號登錄

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章