如何通过第三方QQ登录网站首页
2018-07-03 01:12:05来源:博客园 阅读 ()
QQ登录,就是我们所说的第三方登录,是指用户可以不在本项目中输入密码,而直接通过第三方的验证,成功登录本项目
若想实现QQ登录,需要成为QQ互联的开发者,审核通过才可实现。注册方法参考链接http://wiki.connect.qq.com/%E6%88%90%E4%B8%BA%E5%BC%80%E5%8F%91%E8%80%85
成为QQ互联开发者后,还需创建应用,即获取本项目对应与QQ互联的应用ID,最重要的是拿到APPID,创建应用的方法参考链接http://wiki.connect.qq.com/__trashed-2
申请时的网站域名即项目域名
网站回调域即用户通过扫码后需要跳转到的页面网址
QQ登录开发文档连接http://wiki.connect.qq.com/%E5%87%86%E5%A4%87%E5%B7%A5%E4%BD%9C_oauth2-0
此链接中的网站应用概述中 可以看到效果界面展示,然后点击进入网站开发流程,有5个下拉菜单,前两个是前端用的,后面三个是我们后端要用的
按照步骤获取Access_Token和Open_ID
下面是使用QQ登录的流程图
接下来就是写后台程序了
首先,创建QQ登录模型类,创建一个新的应用oauth,用来实现QQ第三方认证登录。总路由前缀 oauth/
终端进入到/meiduo_mall/apps目录下,使用命令python ../../manage.py startapp oauth 创建子应用oauth,记得创建完之后在settings文件中的INSTALLED_APPS中注册添加此应用,'oauth.apps.OauthConfig'
在meiduo/meiduo_mall/utils/models.py
文件中创建模型类基类,用于增加数据新建时间和更新时间。(为什么要在公共的util中增加models呢,因为在模型类基类中创建的create_time,update_time在后面的商品类等中都会用到,方便调用)
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:
abstract = True # 说明是抽象模型类, 用于继承使用,数据库迁移时不会创建BaseModel的表
接下来,在oauth/models.py中定义QQ身份(openid)与用户模型类User的关联关系
from django.db import models
from meiduo_mall.utils.models import BaseModel
class OAuthQQUser(BaseModel):
"""
QQ登录用户数据
"""
user = models.ForeignKey('users.User', on_delete=models.CASCADE, verbose_name='用户')
openid = models.CharField(max_length=64, verbose_name='openid', db_index=True)#openid其实就是我们当年申请QQ时,QQ服务器为我们生成的那个唯一标识
class Meta:
db_table = 'tb_oauth_qq'
verbose_name = 'QQ登录用户数据'
verbose_name_plural = verbose_name
接下来,终端进入到包含manage.py文件的meiduo_mall目录下,输入迁移命令
进行数据库迁移
python manage.py makemigrations
python manage.py migrate
第一步:点击QQ登录标签,跳转到QQ登录页面
settings配置文件中添加:
# QQ登录参数
QQ_CLIENT_ID = '10147xxxx'
QQ_CLIENT_SECRET = 'c6ce949e04e12ecc909ae6a8b09b637c'
QQ_REDIRECT_URI = 'http://www.xxxx.xxxx:xxxx/oauth_callback.html'
QQ_STATE = '/'
新建oauth/utils.py文件,创建QQ登录辅助工具类
from urllib.parse import urlencode, parse_qs
from urllib.request import urlopen
from itsdangerous import TimedJSONWebSignatureSerializer as Serializer, BadData
from django.conf import settings
import json
import logging
from . import constants
logger = logging.getLogger('django')
class OAuthQQ(object):
""" QQ登录的工具类 """
"""构造方法接收所有的工具方法需要用到的参数"""
def __init__(self, client_id=None, client_secret=None, redirect_uri=None, state=None):
self.client_id = client_id or settings.QQ_CLIENT_ID #appid
self.client_secret = client_secret or settings.QQ_CLIENT_SECRET #appkey
self.redirect_uri = redirect_uri or settings.QQ_REDIRECT_URI
self.state = state or settings.QQ_STATE # 用于保存登录成功后的跳转页面路径
def get_qq_login_url(self):
"""
获取qq登录的网址
:return: url网址
"""
params = {
'response_type': 'code',#此值固定为code,作用是告诉QQ服务器,此用户拿着QQ在扫码,是为了得到一个code,有code才能得到Access_Token
'client_id': self.client_id,#申请QQ登录成功后,分配给应用的appid
'redirect_uri': self.redirect_uri,#网站回调域的网址,必须是注册appid时填写的主域名下的地址,建议设置为网站首页或网站的用户中心。注意需要将url进行URLEncode
'state': self.state, #就是next,# QQ登录成功后回去的地方
'scope':
'get_user_info', #请求用户授权,手机正在扫码的用户,# 标识扫码最终为了获取QQ用户的信息(openid)
}
#拼接QQ扫码登录连接
login_url
= 'https://graph.qq.com/oauth2.0/authorize?' + urlencode(params)
return
login_url
在oauth/views.py中实现视图
# url(r'^qq/authorization/$', views.QQAuthURLView.as_view()),
class QQAuthURLView(APIView):
"""
获取QQ登录的url
"""
def get(self, request):
"""
提供用于qq登录的url
"""
#获取到next参数,实现将来从哪里进入的登录界面,QQ登录成功后,就回到哪里
next = request.query_params.get('next')
oauth = OAuthQQ(state=next)
#生成QQ扫码登录连接(逻辑)
login_url = oauth.get_qq_login_url()
return Response({'login_url': login_url})
子应用中的urls中添加路由
from django.conf.urls import url
from . import views
urlpatterns=[
url(r'^qq/authorization/$', views.QQAuthURLView.as_view()),
]
主业务逻辑的urls中添加子应用路由
urlpatterns = [
#QQ登录
url(r'^oauth/',include('oauth.urls')),
]
urllib使用说明
在后端接口中,我们需要向QQ服务器发送请求,查询用户的QQ信息,Python提供了标准模块urllib可以帮助我们发送http请求。
-
urllib.parse.urlencode(query)
将query字典转换为url路径中的查询字符串
-
urllib.parse.parse_qs(qs)
将qs查询字符串格式数据转换为python的字典
-
urllib.request.urlopen(url, data=None)
发送http请求,如果data为None,发送GET请求,如果data不为None,发送POST请求
返回response响应对象,可以通过read()读取响应体数据,需要注意读取出的响应体数据为bytes类型
第二步:用户扫码登录的回调处理
用户在QQ登录成功后,QQ会将用户重定向回我们配置的回调callback网址,即我们申请QQ登录开发资质时配置的回调地址;
第三步:绑定用户身份接口
业务逻辑:
- 用户需要填写手机号、密码、图片验证码、短信验证码
- 如果用户未在美多商城注册过,则会将手机号作为用户名为用户创建一个美多账户,并绑定用户
- 如果用户已在美多商城注册过,则检验密码后直接绑定用户
注:以下代码合并二三步
新建oauth/serializers.py文件
from rest_framework import serializers from .utils import OAuthQQ from django_redis import get_redis_connection from users.models import User from .models import OAuthQQUser class QQAuthUserSerializer(serializers.Serializer): """ QQ登录创建用户序列化器 """ # 是外界的request.data传过来的注册时的请求体数据 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_save_user_token(access_token) if not openid: raise serializers.ValidationError('无效的access_token') # 将openid放在校验字典中,后面会使用 data['openid'] = openid # 检验短信验证码 mobile = data['mobile'] sms_code = data['sms_code'] redis_conn = get_redis_connection('verify_codes') real_sms_code = redis_conn.get('sms_%s' % mobile) if real_sms_code.decode() != sms_code: raise serializers.ValidationError('短信验证码错误') # 如果用户存在,检查用户密码 try: user = User.objects.get(mobile=mobile) except User.DoesNotExist: pass else: password = data['password'] if not user.check_password(password): raise serializers.ValidationError('密码错误') # 将认证后的user放进校验字典中,后续会使用 data['user'] = user return data def create(self, validated_data): # 获取校验的用户 user = validated_data.get('user') if not user: # 用户不存在,新建用户 user = User.objects.create_user( username=validated_data['mobile'], password=validated_data['password'], mobile=validated_data['mobile'], ) # 将用户绑定openid OAuthQQUser.objects.create( openid=validated_data['openid'], user=user ) # 返回用户数据 return user
新建oauth/exceptions.py文件
class QQAPIException(Exception): """自定义QQ异常""" pass
新建oauth/constants.py文件
# QQ登录保存用户数据的token有效期 SAVE_QQ_USER_TOKEN_EXPIRES=600
使用itsdangerous生成凭据access_token,itsdangerous模块的参考资料连接http://itsdangerous.readthedocs.io/en/latest/
需要安装itsdangerous
pip install itsdangerous
使用TimedJSONWebSignatureSerializer可以生成带有有效期的token
在oauth/utils.py文件
OAuthQQ辅助类中添加方法:
from urllib.parse import urlencode,parse_qs
from urllib.request import urlopen
from .exceptions import QQAPIException
import json
from itsdangerous import TimedJSONWebSignatureSerializer as Serializer, BadData
from .exceptions import QQAPIException
from . import constants
import logging
# 日志记录器
logger = logging.getLogger('django')
def get_access_token(self, code):
"""获取access_token"""
# 准备url
url = 'https://graph.qq.com/oauth2.0/token?'
# 准备参数
params = {
'grant_type':'authorization_code',
'client_id':self.client_id,
'client_secret':self.client_secret,
'code':code,
'redirect_uri':self.redirect_uri
}
# 拼接地址
url += urlencode(params)
try:
# 使用code向QQ服务器发送请求获取access_token
response = urlopen(url)
# 获取响应的二进制
response_data = response.read()
# 将response_data转成字符串
# access_token=FE04************************CCE2&expires_in=7776000&refresh_token=88E4************************BE14
response_str = response_data.decode()
# 将response_str转成字典
response_dict = parse_qs(response_str)
# 提取access_token
# response_dict.get('access_token') == [FE04************************CCE2]
access_token = response_dict.get('access_token')[0]
except Exception as e:
logger.error(e)
raise QQAPIException('获取access_token失败')
return access_token
def get_openid(self, access_token):
"""
使用access_token向QQ服务器请求openid
:param access_token: 上一步获取的access_token
:return: open_id
"""
# 准备url
url = 'https://graph.qq.com/oauth2.0/me?access_token=' + access_token
# 美多向QQ服务器发送请求获取openid
response_str = ''
try:
response = urlopen(url)
response_str = response.read().decode()
# 返回的数据 callback( {"client_id":"YOUR_APPID","openid":"YOUR_OPENID"} )\n;
response_dict = json.loads(response_str[10:-4])
# 获取openid
openid = response_dict.get('openid')
except Exception as e:
# 如果有异常,QQ服务器返回 "code=xxx&msg=xxx"
data = parse_qs(response_str)
logger.error(e)
raise QQAPIException('code=%s msg=%s' % (data.get('code'), data.get('msg')))
return openid
@staticmethod
def generate_save_user_token(openid):
"""
生成保存用户数据的token
:param openid: 用户的openid
:return: token
"""
serializer = Serializer(settings.SECRET_KEY, expires_in=constants.SAVE_QQ_USER_TOKEN_EXPIRES)
data = {'openid': openid}
token = serializer.dumps(data)
return token.decode()
@staticmethod
def check_save_user_token(token):
"""
检验保存用户数据的token
:param token: token
:return: openid or None
"""
serializer = Serializer(settings.SECRET_KEY, expires_in=constants.SAVE_QQ_USER_TOKEN_EXPIRES)
try:
data = serializer.loads(token)
except BadData:
return None
else:
return data.get('openid')
在oauth/views.py中实现视图:
from rest_framework.response import Response
from rest_framework import status
from rest_framework_jwt.views import api_settings
from rest_framework.generics import GenericAPIView
from .utils import OAuthQQ
from .exceptions import QQAPIException
from .models import OAuthQQUser
from . import serializers
# url(r'^qq/user/$', views.QQAuthUserView.as_view()),?
class QQAuthUserView(GenericAPIView):
"""用户扫码登录的回调处理"""
# 指定序列化器
serializer_class = serializers.QQAuthUserSerializer
def get(self, request):
# 提取code请求参数
code = request.query_params.get('code')
if not code:
return Response({'message':'缺少code'}, status=status.HTTP_400_BAD_REQUEST)
# 创建QQ登录的工具对象
oauth = OAuthQQ()
try:
# 使用code向QQ服务器请求access_token
access_token = oauth.get_access_token(code)
# 使用access_token向QQ服务器请求openid
openid = oauth.get_openid(access_token)
except QQAPIException:
return Response({'message':'QQ服务异常'}, status=status.HTTP_503_SERVICE_UNAVAILABLE)
# 使用openid查询该QQ用户是否在美多商城中绑定过用户
try:
oauth_user = OAuthQQUser.objects.get(openid=openid)
except OAuthQQUser.DoesNotExist:
# 如果openid没绑定美多商城用户,创建用户并绑定到openid
access_token_openid = OAuthQQ.generate_save_user_token(openid)
return Response({'access_token':access_token_openid})
else:
# 如果openid已绑定美多商城用户,直接生成JWT token,并返回
jwt_payload_handler = api_settings.JWT_PAYLOAD_HANDLER
jwt_encode_handler = api_settings.JWT_ENCODE_HANDLER
# 获取关联openid的user
user = oauth_user.user
payload = jwt_payload_handler(user)
token = jwt_encode_handler(payload)
# 向前端响应token, user_id,username
return Response({
'token':token,
'user_id':user.id,
'username':user.username
})
def post(self, request):
"""给openid绑定用户数据"""
# 获取序列化器:注册的数据都在POST请求的请求体里面
serializer = self.get_serializer(data=request.data)
# 开启校验
serializer.is_valid(raise_exception=True)
# 保存校验的数据 : create会返回user
user = serializer.save()
# 生成JWT token 响应
jwt_payload_handler = api_settings.JWT_PAYLOAD_HANDLER
jwt_encode_handler = api_settings.JWT_ENCODE_HANDLER
payload = jwt_payload_handler(user)
token = jwt_encode_handler(payload)
# 向前端响应token, user_id,username
return Response({
'token': token,
'user_id': user.id,
'username': user.username
})
在oauth/urls中 添加路由
urlpatterns=[
# 获取QQ扫码登录连接
url(r'^qq/authorization/$', views.QQAuthURLView.as_view()),
# 获取QQ登录用户信息(code,access_token, openid)
url(r'^qq/user/$', views.QQAuthUserView.as_view()),
]
最后再上一张绑定QQ身份的处理流程图供参考
标签:
版权申明:本站文章部分自网络,如有侵权,请联系:west999com@outlook.com
特别注意:本站所有转载文章言论不代表本站观点,本站所提供的摄影照片,插画,设计作品,如需使用,请与原作者联系,版权归原作者所有
- 小白如何入门 Python 爬虫? 2019-08-13
- Django项目中使用qq第三方登录。 2019-08-13
- 我是如何自学python到找到高薪工作 2019-07-24
- 如何使用Python脚本分析CPU使用情况的? 2019-07-24
- 学Python要避免哪些坑,如何巩固好基础 2019-07-24
IDC资讯: 主机资讯 注册资讯 托管资讯 vps资讯 网站建设
网站运营: 建站经验 策划盈利 搜索优化 网站推广 免费资源
网络编程: Asp.Net编程 Asp编程 Php编程 Xml编程 Access Mssql Mysql 其它
服务器技术: Web服务器 Ftp服务器 Mail服务器 Dns服务器 安全防护
软件技巧: 其它软件 Word Excel Powerpoint Ghost Vista QQ空间 QQ FlashGet 迅雷
网页制作: FrontPages Dreamweaver Javascript css photoshop fireworks Flash