DRF框架QQ登录功能
用户模块---QQ登录\
流程图
QQ登录文档:http://wiki.connect.qq.com/%E5%87%86%E5%A4%87%E5%B7%A5%E4%BD%9C_oauth2-0
流程简述:
1.当点击qq登录图标时,进入生成登录url的接口
2.在前端的回调函数中跳转到qq登录的扫码页面
3.扫码登陆后,qq会携带code访问申请时指定的回调地址,
使用code获取access_token
使用access_token最终获取openid
4用来判断用户是否是第一次使用QQ登录
5.是,返回绑定/注册,页面第一次使用QQ登录,额外判断,是否已经注册账号
5.1是,统一跳转到绑定/注册界面,
5.1.1将openid和账号进行绑定/注册,手动调用jwt功能返回jwt,id,username
5.2否,返回首页,手动调用jwt功能返回jwt,id,username
具体实现:
使用到的模块:
import urllib
urllib.parse.urlencode(query) # 将字典转换为url路径中的查询字符串
urllib.parse.parse_qs(qs) # 将查询字符串格式数据转换为python的字典
urllib.request.urlopen(url, data=None)
在python后端发起http请求,
如果data为None,发送GET请求,
如果data不为None,发送POST请求
response.read().decode()
返回response响应对象,可以通过read()读取响应体数据,需要注意读取出的响应体数据为bytes类型
手动调用jwt生成token用于验证登录状态
from rest_framework_jwt.settings import api_settings
jwt_payload_handler = api_settings.JWT_PAYLOAD_HANDLER
jwt_encode_handler = api_settings.JWT_ENCODE_HANDLER
payload = jwt_payload_handler(user)
jwt_token = jwt_encode_handler(payload)
itsdangerous模块用于生成token和解析token
from itsdangerous import TimedJSONWebSignatureSerializer
serializer = TimedJSONWebSignatureSerializer(settings.SECRET_KEY, 300)
# 需要转换成token的数据
data = {'openid': openid}
# .dumps生成token,bytes类型,需要解码
token = serializer.dumps(data).decode()
# .loads将token解析为需要的数据,
openid = serializer.loads(token).get('openid')
模型类,保存QQ的openid和本站用户之间的关系:
from django.db import models
class BaseModel(models.Model):
"""基类模型"""
create_time = models.DateTimeField(auto_now_add=True, verbose_name="创建时间")
update_time = models.DateTimeField(auto_now=True, verbose_name="更新时间")
class Meta:
# 指定BaseModel为抽象类,不会创建实体表
abstract = True
class OAuthQQUser(BaseModel):
"""QQ登录的模型"""
openid = models.CharField(max_length=64, verbose_name='openid', db_index=True)
user = models.ForeignKey('user.User', on_delete=models.CASCADE, verbose_name='用户')
objects = models.Manager()
class Meta:
db_table = 'tb_oauth_qq'
verbose_name = 'QQ登录用户数据'
verbose_name_plural = verbose_name
定义一个工具类,主要负责生成token和验证token是否正确
class OAuthQQ(object):
"""用于qq登录的工具类"""
def __init__(self, client_id=None, client_secret=None, redirect_uri=None, state=None):
"""QQ登录开发文档中需要的参数"""
self.client_id = client_id or settings.QQ_CLIENT_ID
self.client_secret = client_secret or settings.QQ_CLIENT_SECRET
self.redirect_uri = redirect_uri or settings.QQ_REDIRECT_URI
self.state = state or settings.QQ_STATE # 用于保存登录成功后的跳转页面路径
在OAuthQQ工具类中新增生成登录url的方法
def generate_qq_login_url(self):
"""生成用于qq登录扫码的url地址"""
params = {
'response_type': 'code', # 默认值
'client_id': self.client_id,
'redirect_uri': self.redirect_uri,
'state': self.state,
'scope': 'get_user_info', # 用户勾选的授权范围,get_user_info表示,获取登录用户的昵称、头像、性别
}
url = 'https://graph.qq.com/oauth2.0/authorize?'
# 拼接查询字符串,
url += parse.urlencode(params)
return url
定义返回扫码QQ登录url的接口
# 在点击qq登录图标时向接口发起请求
# 后端生成用于QQ扫码登录的页面的url地址
# 在前端回调函数中执行
# GET /oauth/qq/authorization/?state=xxx
class QQAuthUrlView(APIView):
"""获取QQ扫码登录的网址接口"""
def get(self, request):
"""
:return 扫码的url地址
"""
state = request.query_params.get('state')
oauthqq = OAuthQQ(state=state)
qq_login_url = oauthqq.generate_qq_login_url()
return Response({'qq_login_url': qq_login_url})
前端将code当参数传入后端接口,生成获取access_token的url
在OAuthQQ工具类新增,使用code请求并获取QQ的access_token,的方法
def get_qq_access_token(self, code):
"""获取access_token"""
params = {
'grant_type': 'authorization_code',
'client_id': self.client_id,
'client_secret': self.client_secret,
'code': code,
'redirect_uri': self.redirect_uri,
}
url = 'https://graph.qq.com/oauth2.0/token?'
url += parse.urlencode(params)
# 向qq方发起http请求,获取包含access_token的查询字符串
# 形式access_token=FE04************************CCE2&expires_in=7776000&refresh_token=88E4************************BE14
try:
response = request.urlopen(url)
response_data = response.read().decode()
# 讲查询字符串转换为python中的字典,[{}]
data = parse.parse_qs(response_data)
access_token = data.get('access_token', None)[0]
except Exception as e:
logger.error(e)
raise Exception('获取access_token异常')
return access_token
根据access_token生成获取openid的url
后端发送http请求,从返回值中获取openid
def get_qq_openid(self, access_token):
"""获取openid"""
url = 'https://graph.qq.com/oauth2.0/me?access_token='
url += access_token
logger.error(url)
try:
response = request.urlopen(url)
response_data = response.read().decode()
# 返回一个字符串 callback( {"client_id":"YOUR_APPID","openid":"YOUR_OPENID"} )\n;
data_dict = json.loads(response_data[10:-4])
openid = data_dict.get('openid', None)
except Exception as e:
logger.error(e)
raise Exception('获取openid异常')
return openid
根据openid去判断,该用户是否是第一次使用QQ登录功能.
定义类视图和serializers序列化器
class QQAuthUserView(GenericAPIView):
"""QQ登录后接口"""
serializer_class = OAuthQQUserSerializer
def get(self, request):
"""QQ登录"""
code = request.query_params.get('code')
if not code:
return Response({'message': 'code不存在'}, 400)
# 目标是通过 code获取access_token
oauthqq = OAuthQQ()
access_token = oauthqq.get_qq_access_token(code)
# 通过access_token获取openid
openid = oauthqq.get_qq_openid(access_token)
# 获取openid后需要判断
# oauthqquser = OAuthQQUser.get
try:
oauthqquser = OAuthQQUser.objects.get(openid=openid)
except Exception as e:
logger.error('此人未绑定或未注册:%s' % e)
# 1.第一次用qq登录
# 使用openid生成记录qq身份的token,以便注册或绑定时验证身份
access_token = OAuthQQ.generate_save_user_token(openid)
return Response({'access_token': access_token})
# 1.1 已经注册本站账号--->跳转绑定界面
# 1.2 未注册本站账号--->注册并绑定
# 2.以前已经qq登录过(一定有本站账号)
else:
user = oauthqquser.user
# 生成jwt_token,用于记录登录状态
jwt_payload_handler = api_settings.JWT_PAYLOAD_HANDLER
jwt_encode_handler = api_settings.JWT_ENCODE_HANDLER
payload = jwt_payload_handler(user)
jwt_token = jwt_encode_handler(payload)
data = {
'user_id': user.id,
'username': user.username,
'token': jwt_token
}
return Response(data=data)
def post(self, request):
"""QQ账号绑定和新增功能"""
serializer = self.get_serializer(data=request.data)
serializer.is_valid(raise_exception=True)
user = serializer.save()
jwt_payload_handler = api_settings.JWT_PAYLOAD_HANDLER
jwt_encode_handler = api_settings.JWT_ENCODE_HANDLER
payload = jwt_payload_handler(user)
jwt_token = jwt_encode_handler(payload)
data = {
'user_id': user.id,
'username': user.mobile,
'token': jwt_token
}
return Response(data=data)
序列化器
class OAuthQQUserSerializer(serializers.Serializer):
"""创建或绑定QQ对应的本站用户"""
access_token = serializers.CharField(label='操作凭证')
mobile = serializers.RegexField(label='手机号', regex=r'^1[3-9]\d{9}$')
password = serializers.CharField(label='密码', max_length=20, min_length=8)
sms_code = serializers.CharField(label='短信验证码')
def validate(self, data):
# 检验access_token
access_token = data['access_token']
openid = OAuthQQ.check_token_by_openid(access_token)
if not openid:
raise serializers.ValidationError('access_token失效')
# 检验短信验证码
mobile = data['mobile']
sms_code = data['sms_code']
redis_conn = get_redis_connection('verify_codes')
real_sms_code = redis_conn.get('sms_%s' % mobile).decode()
if not real_sms_code:
raise serializers.ValidationError('短信验证码失效或过期')
if real_sms_code != sms_code:
raise serializers.ValidationError('短信验证码错误')
# 如果用户存在,检查用户密码
try:
user = User.objects.get(mobile=mobile)
except Exception as e:
logger.error('本站用户不存在,等待注册---%s' % e)
pass
else:
# 如果存在就校验密码
password = data['password']
if not user.check_password(password):
raise serializers.ValidationError('密码错误')
data['user'] = user
data['openid'] = openid
return data
def create(self, validated_data):
user = validated_data.get('user', None)
if not user:
# 用户不存在,先注册本站新用户
user = User.objects.create_user(
username=validated_data['mobile'],
password=validated_data['password'],
mobile=validated_data['mobile'],
)
# 新老用户都绑定QQ的openid
OAuthQQUser.objects.create(
openid=validated_data['openid'],
user=user
)
return user
DRF框架QQ登录功能的更多相关文章
- 网站集成QQ登录功能
最近在做一个项目时,客户要求网站能够集成QQ登录的功能,以前没做过这方面的开发,于是去QQ的开放平台官网研究了一下相关资料,经过自己的艰苦探索,终于实现了集成QQ登录的功能,现在把相关的开发经验总结一 ...
- 网站集成QQ登录功能(转)
最近在做一个项目时,客户要求网站能够集成QQ登录的功能,以前没做过这方面的开发,于是去QQ的开放平台官网研究了一下相关资料,经过自己的艰苦探索,终于实现了集成QQ登录的功能,现在把相关的开发经验总结一 ...
- QQ登录功能之如何获取用于本地测试的APPID
本文主要说明一下开发者如何在QQ互联创建测试应用,从而分配给我们一套APP ID和APP KEY,在我们平时学习的时候使用. 一.QQ互联注册开发者 要想使用QQ登陆的功能,首先你必须是腾讯开发者.腾 ...
- DRF框架中分页功能接口
目录 DRF框架中分页功能接口 DRF框架中分页功能接口 一.在框架中提供来三个类来实现分页功能,PageNumberPagination.LimitOffsetPagination.CursorPa ...
- React Native 接入微博、微信、QQ 登录功能
在 App 开发中我们经常需要在用户登录模块接入 SNS 登录组件,这样会大大提高用户的注册体验.特别当一个不是刚性需求 App 推广的时候,这样会很大的降低用户体验的成本,没有人愿意忍受输入邮箱.手 ...
- jeecms框架单点登录功能的实现
单点登录的功能实现主要原理: 1: 在点击登录按钮的时候使用reponse.addCookie()方法向浏览器发送cookie: 2: 在前段拦截器中的request.getCookie()在接收到设 ...
- 接入qq登录功能出现的问题
在调用qq授权的接口时,出现以上错误. 原因是: 打包的应用签名和第一次上传包的签名不一致造成的 解决方法: 第一种方法:用上次打包apk的keystore重新打包apk,使签名一致. 第二种方法:联 ...
- 安卓应用使用QQ登录的申请流程
“QQ互联”是腾讯为第三方网站.媒体.终端提供的开放平台.QQ互联拥有8个组件,提供诸如分享.登陆.like.qq提醒等能力.开发者使用QQ帐号登陆组件可以降低了用户的注册门槛,减少注册环节的用户流失 ...
- h5 网页版的微博微信QQ登录
一:微博 1,先说微博吧,首先你的去http://open.weibo.com/wiki/先注册账号,通过验证审核.然后的创建网页应用.微博审核不通过的原因就是域名和网站地址,一定要按实际写的.一定要 ...
随机推荐
- 【C语言】 8421BCD码与二进制的转换
#define BCD2TODEC(bcd) (bcd) = ((bcd) & 0x0f)+ ((bcd)>>4)*10 #define BIN2BCD(bcd) (bcd) = ...
- 再谈全局网HBase八大应用场景
摘要: HBase可以说是一个数据库,也可以说是一个存储.拥有双重属性的HBase天生就具备广阔的应用场景.在2.0中,引入了OffHeap降低了延迟,可以满足在线的需求.引入MOB,可以存储10M左 ...
- jQuery插件实例五:手风琴效果[动画效果可配置版]
昨天写了个jQuery插件实例四:手风琴效果[无动画版]那个是没有动画效果的,且可配置性不高,本篇为有动画效果.对于一些数据做了动态的计算,以实现自适应. 欢迎大家入群相互交流,学习,新群初建,欢迎各 ...
- November 04th, 2017 Week 44th Saturday
It does not do to dwell on dreams and forget to live. 整天沉溺于梦想而忘记如何好好生活,毫无意义. Bingo, and I think it i ...
- 编程算法 - 左旋转字符串 代码(C)
版权声明:本文为博主原创文章.未经博主同意不得转载. https://blog.csdn.net/u012515223/article/details/37689725 左旋转字符串 代码(C) 本文 ...
- VC++ MFC单文档应用程序SDI下调用glGenBuffersARB(1, &pbo)方法编译通过但执行时出错原因分析及解决办法:glewInit()初始化的错误
1.问题症状 在VC++环境下,利用MFC单文档应用程序SDI下开发OpenGL程序,当调用glGenBuffersARB(1, &pbo)方法编译通过但执行时出错,出错代码如下: OpenG ...
- MVC 拦截指定的action
有时,我们需要在特定的一些aciton中做校验.比如:验证是否登录.实现方式有两种: 一.编写一个公共的方法专门用于实现是否登录的验证,然后在每个需要进行验证的aciton的头部去调用该方法,根据方法 ...
- MVC的BundleConfig应用
1.MVC可以通过BundleConfig类来配置css和js的统一引用,分别通过StyleBundle和ScriptBundle来创建. 2.可以在母版页中统一加载设置在BundleConfig.c ...
- JQuery radio单选框应用
转载:JQuery判断radio(单选框)是否选中和获取选中值方法总结 一.利用获取选中值判断选中 直接上代码,别忘记引用JQuery包 复制代码 代码如下: < !DOCTYPE html P ...
- SystemView SEGGER FreeRTOS 移植和使用
/* 官方帮助英文翻译文档参考:https://blog.csdn.net/bjr2016/article/category/7275877. */ /* 移植文档参考:https://blog.cs ...