django项目之 集成QQ三方登录

一、流程图

二、新建一个单独的三方登录模型,用于以后集中使用三方登录

  官方文档: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)
世间安得双全法,不负如来不负卿
原文地址:https://www.cnblogs.com/shangguanruoling/p/12171525.html