这一篇教程,我们一起使用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第三方账号登录