qq目前没有为大家提供SDK 文档 来供大家直接使用 : 只提供了 参数获取的 的接口文档, 这里为大家提供一个封装好的 SDK
直接调用即可 .
详细参见 : https://gitee.com/y2030/qq_auxiliary_login_sdk.git
from urllib.parse import urlencode, parse_qs
from urllib.request import urlopen
import json
from django.conf import settings
from itsdangerous import TimedJSONWebSignatureSerializer as Serializer, BadData
from . import constants
import logging
# 日志记录器
from .exceptions import QQAPIException
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
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
def get_qq_login_url(self):
"""
获取qq登录的网址
QQ登录参数:
QQ_CLIENT_ID = '101474184'
QQ_CLIENT_SECRET = 'c6ce949e04e12ecc909ae6a8b09b637c'
QQ_REDIRECT_URI = 'http://www.meiduo.site:8080/oauth_callback.html'
QQ_STATE = '/'
:return: url
"""
# 1, 准备url: 必须自己补充?
login_url = 'https://graph.qq.com/oauth2.0/authorize?'
# 2, 准备参数
# 'https://graph.qq.com/oauth2.0/authorize?response_type=code&client_id=xx..
params = {
'response_type': 'code', # 表示扫码后未来得到的code(获取access_token的凭据)
'client_id': self.client_id, # 创建应用时 的标识appid
'redirect_uri': self.redirect_uri, # 扫码成功后回调地址
'state': self.state, # next参数, client端的状态值。用于第三方应用防止CSRF攻击
'scope': 'get_user_info',
}
login_url += urlencode(params)
return login_url
def get_access_token(self, code):
"""
获取access_token
:param code:qq提供的code
:return:access_token
"""
# 准备url PC网站:https://graph.qq.com/oauth2.0/token
get_token_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
}
try:
get_token_url += urlencode(params)
# 使用code向QQ服务器发送请求 读取到的数据为bytes类型
response_data = urlopen(get_token_url).read()
# 转码成字符串类型
response_str = response_data.decode()
# 将查询字符串格式数据转换为python的字典
response_dict = parse_qs(response_str)
# 从字典中取出access_token
# access_token=FE04***************CCE2 & expires_in = 7776000 & refresh_token = 88E4****************BE14
# response_dict.get('access_token')==[FE04 ************************ CCE2]
access_token = response_dict.get('access_token')[0]
except Exception as e:
logger.error(e)
# 抛出异常,在views中, 谁调用谁try
raise QQAPIException('获取access_token失败')
return access_token
def get_openid(self, access_token):
"""
获取用户的openid
:param access_token: qq提供的access_token
:return: open_id
"""
# 准备url
get_openid_url = 'https://graph.qq.com/oauth2.0/me?access_token=' + access_token
# 使用access_token向QQ服务器发送请求, 读取到的是bytes类型, 将bytes类型的数据转码为字符串
response_str = urlopen(get_openid_url).read().decode()
try:
# callback({"client_id": "YOUR_APPID", "openid": "YOUR_OPENID"});
response_dict = json.loads(response_str[10:-4])
except Exception:
data = parse_qs(response_str)
logger.error('code=%s msg=%s' % (data.get('code'), data.get('msg')))
raise QQAPIException
# 从字典中取出openid
openid = response_dict.get('openid', None)
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_EXPORES)
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_EXPORES)
try:
data = serializer.loads(token)
except BadData:
return None
else:
return data.get('openid')
---------------------------------------------------------------------------------------------------------------------------------
在view.py中
from django.shortcuts import render
from rest_framework import status
from rest_framework.generics import GenericAPIView
from rest_framework.response import Response
from rest_framework.views import APIView
from .utils import OAuthQQ
from .exceptions import QQAPIException
from .models import OAuthQQUser
from rest_framework_jwt.views import api_settings
from . import serializers
from carts.utils import merge_cart_cookie_to_redis
# Create your views here.
# 第一大步
# 只负责拼接字符串, 不需要验证, 所以用APIView
# GET /oauth/qq/authorization/?next=xxx
# url(r'^qq.authorization/$', views.QQAuthURLView.as_view())
class QQAuthURLView(APIView):
"""
获取QQ扫码登录界面的的url
"""
def get(self, request):
"""
提供用于qq登录的url
"""
next = request.query_params.get('next')
print(next)
# 创建qq登录的工具类的对象
oauth = OAuthQQ(state=next)
# 生成QQ扫码登录的链接
login_url = oauth.get_qq_login_url()
return Response({'login_url': login_url})
# http://www.xxxx.com:8080/oauth_callback.html?code=468FE2461369C40E4EDA0813CCBDCEE9&state=%2F
# GET /oauth/qq/user/?code=xxx
# url(r'^qq/user/$', views.QQAuthUserView.as_view()),
class QQAuthUserView(GenericAPIView):
"""⽤户扫码登录的回调处理"""
# 指定序列化器
serializer_class = serializers.OAuthQQUserSerializer
# 第二大步
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的对象
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)
response = Response({
"user_id": user.id,
"username": user.username,
"token": token
})
# 向前端相应token, user_id, username
return response
# 本次创建用户模型类有两个, 所以不使用CreateAPIView
def post(self, request):
"""用户扫码登录的回调处理"""
# 获取序列化器 , 注册的数据都在post请求的请求体里面
serializer = self.get_serializer(data=request.data)
# 开启校验
serializer.is_valid(raise_exception=True)
# 保存校验的数据 : create 会返回user
user = serializer.save()
# 生成已经登录的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)
response = Response({
'token': token,
'user_id': user.id,
'username': user.username
})
return response