一、流程图
二、新建一个单独的三方登录模型,用于以后集中使用三方登录
官方文档:https://wiki.connect.qq.com/oauth2-0%E5%BC%80%E5%8F%91%E6%96%87%E6%A1%A3
操作流程按照如下步骤进行即可:
三、代码部分:前端按钮部分不处理,本文只负责后端的接口:
1、请求https://graph.qq.com/oauth2.0/authorize接口,参数和请求方法见官方文档,获取access_token
# 再utils文件中定义,方便后续代码的迁移或者框架改造后的复用
from django.conf import settings
from urllib.parse import urlencode, parse_qs
from urllib.request import urlopen
import json
import logging
logger = logging.getLogger("django")
class QQErrorExcept(Exception):
"""自定义一个QQ异常类"""
pass
class OauthQQ():
"""QQ的辅助类工具,单独封装"""
def __init__(self, app_key=None, appid=None, redirect_uri=None, state=None):
self.app_key = app_key or settings.QQ_APP_KEY
self.appid = appid or settings.QQ_APP_ID
self.redirect_uri = redirect_uri or settings.QQ_REDIRECT_URL
self.state = state or settings.QQ_STATE
def get_QQ_url(self):
"""用于获取QQ的链接,返回给客户端调用获取access_token"""
params = {
"response_type": "code",
"client_id": self.appid,
"redirect_uri": self.redirect_uri,
"state": self.state,
"scope": "get_user_info"
}
url = "https://graph.qq.com/oauth2.0/authorize?" + urlencode(params)
return url
def get_QQ_access_token(self, code, state=None):
"""
根据授权码,获取临时票据access_token
:param code: 前段获取到并传给后端的authencation code
:param state: 回调参数
:return:
"""
params = {
"grant_type": "authorization_code",
"client_id": self.appid,
"client_secret": self.app_key,
"code": code,
"redirect_uri": self.redirect_uri
}
url = "https://graph.qq.com/oauth2.0/token?" + urlencode(params)
try:
response = urlopen(url)
response_data = response.read().decode()
# parse_qs 把查询字符串格式的内容转换成字典[注意:转换后的字典,值是列表格式]
data = parse_qs(response_data)
access_token = data.get("access_token")[0]
expires_in = data.get("expires_in")[0]
refresh_token = data.get("refresh_token")[0]
except:
logger.error('code=%s msg=%s' % (data.get("code"), data.get('msg')))
raise QQErrorExcept
return access_token
def get_openid(self, access_token):
url = "https://graph.qq.com/oauth2.0/me?access_token=" + access_token
try:
response = urlopen(url)
response_data = response.read().decode()
data = json.loads(response_data[10:-4])
openid = data.get("openid")
except:
logger.error('code=%s msg=%s' % (data.get('code'), data.get('msg')))
raise QQErrorExcept
return openid
def get_qq_userinfo(self,access_token=None, openid=None):
"""根据access_token和openid获取用户信息"""
params = {
'access_token': access_token,
'oauth_consumer_key': self.appid,
'openid': openid,
}
url = 'https://graph.qq.com/user/get_user_info?' + urlencode(params)
try:
response = urlopen(url)
response_data = response.read().decode()
data = json.loads(response_data)
return data
except:
logger.error('code=%s msg=%s' % (data.get('code'), data.get('msg')))
raise QQErrorExcept
2、再视图Views文件中调用:
from django.shortcuts import render from rest_framework.views import APIView from rest_framework.response import Response from rest_framework import status from urllib.request import urlopen from rest_framework_jwt.views import api_settings from .utils.oauthQQ import OauthQQ, QQErrorExcept from .models import OauthUser from users.models import User import random # Create your views here. class OauthQQAPIView(APIView): def get(self, request): state = request.query_params.get("state") oauth = OauthQQ(state=state) url = oauth.get_QQ_url() return Response({"url": url}, status=status.HTTP_200_OK) class OauthQQInfoAPIView(APIView): """根据前端获取到的code,后端获取access_token和用户信息""" def get(self, request): code = request.query_params.get("code") state = request.query_params.get("state") oauth = OauthQQ(state=state) try: access_token = oauth.get_QQ_access_token(code=code, state=state) openid = oauth.get_openid(access_token=access_token) qq_user_info = oauth.get_qq_userinfo(access_token=access_token, openid=openid) except QQErrorExcept: return Response({"message": "QQ登录异常,获取授权信息失败"}, status=status.HTTP_400_BAD_REQUEST) #根据获取到的openid去数据库查找是否有该用户 try: oauth_user = OauthUser.objects.get(openid=openid) user = oauth_user.user # 用户存在,则返回jwt,登录成功 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) user_info = { "id": user.id, "nickname": user.nickname, "avatar": user.avatar.url, "token": token, "username": user.username } return Response({"user_info": user_info}, status=status.HTTP_200_OK) except OauthUser.DoesNotExist: # 如果QQ用户信息不存在,那么直接使用qq_nickname随机注册一个账号给用户 nickname = qq_user_info.get("nickname")+"_"+str(random.randint(0000,9999)) try: user = User.objects.create_user(nickname=nickname, username=nickname) oauth_user = OauthUser.objects.create(user=user, openid=openid) 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) user_info = { "id": user.id, "nickname": user.nickname, "avatar": user.avatar.url, "token": token, "username": user.username } except: return Response({"message": "创建用户失败"}, status=status.HTTP_500_INTERNAL_SERVER_ERROR) return Response({"user_info": user_info}, status=status.HTTP_200_OK)