Django之第三方平台QQ授权登录的实现
作者:CodeDevMaster 发布时间:2023-04-27 16:37:06
环境准备
创建QQ互联应用
创建一个QQ互联应用,并获取到App ID和App Key。
QQ互联官网:https://connect.qq.com/
开发文档:https://wiki.connect.qq.com/
创建应用模块
创建一个新的应用oauth,用来实现QQ第三方认证登录的代码编写。
python manage.py startapp oauth
在settings.py中注册应用
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设置路由
url(r'^', include('apps.oauth.urls',namespace='oauth')),
定义QQ登录模型类
在oauth/models.py中定义QQ身份(openid)与用户模型类User的关联关系
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="创建时间")
update_time = models.DateTimeField(auto_now=True, verbose_name="更新时间")
class Meta:
db_table = 'tb_oauth_qq'
verbose_name = 'QQ登录用户'
verbose_name_plural = verbose_name
执行迁移
执行迁移操作,生成QQ登录模型类对应的数据库表
python manage.py makemigrations
python manage.py migrate
QQLoginTool库
腾讯QQ互联平台没有Python SDK,但是可以使用第三方封装好的SDK包,QQLoginTool是一个第三方库,封装了对接QQ互联的请求操作,可用于快速实现QQ登录的一种工具包。
安装QQLoginTool
pip install QQLoginTool
API使用说明
导入
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登录参数
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登录页面网址
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: 从哪个页面进入到的登录页面,登录成功后自动回到那个页面
next = request.GET.get('next')
# 获取QQ登录页面网址
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})
认证获取openid
1.用户在QQ登录成功后,QQ会将用户重定向到配置的回调网址,同时会传递一个Authorization Code
2.拿到Authorization Code并完成OAuth2.0认证获取openid
注意:回调网址在申请QQ登录开发资质时进行配置
from django.urls import re_path
from . import views
urlpatterns = [
re_path(r'^oauth/callback/$', views.QQAuthUserView.as_view()),
]
使用code向QQ服务器请求,获取access_token
使用access_token向QQ服务器请求获取openid
"""用户扫码登录的回调处理"""
class QQAuthUserView(View):
def get(self, request):
"""Oauth2.0认证"""
# 提取code请求参数
code = request.GET.get('code')
if not code:
return http.HttpResponseBadRequest('缺少code')
# 创建oauth 对象
oauth = OAuthQQ(client_id=settings.QQ_CLIENT_ID, client_secret=settings.QQ_CLIENT_SECRET,
redirect_uri=settings.QQ_REDIRECT_URI)
try:
# 使用code向QQ服务器请求access_token
access_token = oauth.get_access_token(code)
# 使用access_token向QQ服务器请求openid
openid = oauth.get_open_id(access_token)
except Exception as e:
logger.error(e)
return http.HttpResponseServerError('OAuth2.0认证失败')
pass
openid的判断处理
openid是否绑定过用户
判断openid是否绑定过用户,只需要使用openid查询该QQ用户是否绑定过用户即可。
oauth_user = OAuthQQUser.objects.get(openid=openid)
openid已绑定用户
如果openid已绑定用户,直接生成状态保持信息,登录成功,并重定向到首页。
try:
oauth_user = OAuthQQUser.objects.get(openid=openid)
except OAuthQQUser.DoesNotExist:
# 如果openid没绑定用户
pass
else:
# 如果openid已绑定用户,实现状态保持
qq_user = oauth_user.user
login(request, qq_user)
# 响应结果
next = request.GET.get('state')
response = redirect(next)
# 登录时用户名写入到cookie,有效期15天
response.set_cookie('username', qq_user.username, max_age=3600 * 24 * 15)
return response
openid未绑定用户
openid属于用户隐私信息,在后续的绑定用户操作中前端会使用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 response
oauthCallback.html中渲染access_token
<input type="hidden" name="access_token" value="{{ access_token }}">
openid签名处理
签名处理可以使用itsdangerous库,它是一个用于Python语言的库,提供了一些安全传输数据的工具类。其主要功能是在保证数据安全性的前提下,生成认证令牌、时间限制的令牌和加密/解密数据信息等。
安装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(数据), 返回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绑定用户的过程类似于用户注册的业务逻辑
class QQAuthUserView(View):
"""用户扫码登录的回调处理"""
def get(self, request):
"""Oauth2.0认证"""
......
def post(self, request):
"""用户绑定openid"""
# 接收参数
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')
# 判断参数是否齐全
if not all([mobile, password, sms_code_client]):
return http.HttpResponseBadRequest('缺少必传参数')
# 判断手机号是否合法
if not re.match(r'^1[3-9]\d{9}$', mobile):
return http.HttpResponseBadRequest('请输入正确的手机号码')
# 判断密码是否合格
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'})
# 保存注册数据
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登录失败'})
# 实现状态保持
login(request, user)
# 响应绑定结果
next = request.GET.get('state')
response = redirect(next)
# 登录时用户名写入到cookie,有效期15天
response.set_cookie('username', user.username, max_age=3600 * 24 * 15)
return response
来源:https://juejin.cn/post/7228933966258405433
猜你喜欢
- 1下载安装1.1打开官网http://www.jetbrains.com/pycharm/download/#section=windows
- 上篇博文我们实现了两百行代码实现贪吃蛇游戏,这次我们来实现一个代码量更加少,功能却更加完整的python代码实现贪吃蛇游戏,具体代码与文件可
- firefox不支持text-overflow一直让人很折腾。。不过还好有大虾为我们提供解决方案。。text-overflow: ellip
- 游戏截图动态演示源码分享state/tool.pyimport osimport jsonfrom abc import abstractm
- 准备篇:CentOS 6.6系统安装配置图解教程https://www.jb51.net/os/239738.html一、配置防火墙,开启8
- python包含子目录中的模块方法比较简单,关键是能够在sys.path里面找到通向模块文件的路径。下面将具体介绍几种常用情况:(1)主程序
- * test11.pyimport timeprint "1"time.sleep(2)print "1&qu
- 模板的继承完美在写html的时候会发现,自己多个html文件中又好多东西是一样的,包括静插件的引入 还有有些简单的css样式都不需要修改,这
- 如何在ADO中使用存储查询?对于使用参数存贮查询,我们可用下面的代码进行示例:Private cn As Ne
- 一、数据类型分类1、按存值个数区分单个值:数字,字符串多个值(容器):列表,元组,字典,集合2、按可变不可变区分可变:列表[],字典{},集
- 在Python 3.10发布之前,Python是没有类似于其他语言中switch语句的,要实现类似的功能最简单的方法就是通过if ... e
- 前文昨天家里来人,老姐的小孩儿抢着跟我玩电脑,result........很久很久之后!!那你想错了,我可不是欺负小孩子的那种人。老实人本人
- 首先,必须有错误继续进行的声明On Error Resume Next 然后尝试简历jmail实例: Dim JMail Set JMail
- 如何把imagenet预训练的模型,输入层的通道数随心所欲的修改,从而来适应自己的任务#增加一个通道w = layers[0].weight
- 前言matplotlib画图例默认的位置是在图中的各个角落,但有时图例位置会遮挡住图像而不符合我们的需求,需要对图例位置进行调整。代码如下:
- 本文的目的是探讨JS相关技术,并不是以杀毒为主要目的,杀毒只是为讲解一些JS做铺垫的,呵呵,文章有点长,倒杯咖啡或者清茶慢慢看,学习切勿急躁
- 使用 Python 内建的defaultdict 方法可以轻松定义一个树的数据结构。简单的说树也可以是一个字典数据结构 def t
- 打算学习linux和考一下认证。学习HCIA-AI实验手册发现的小问题和记录贴,防止自己忘。我不知道这个手册是不是公开的,你们自己去华为下载
- Python操作Excel之openpyxlopenpyxl是一个Python库,用来读写Excel2010 xlsx/xlsm/xltx/
- 用python读取视频有两种主要方法,大家可依据自己的需求进行使用。方法一:使用imageio库,没有安装的可用pip安装或自己下载,安装好