【Django】—QQ登录

1、QQ登录逻辑分析

在这里插入图片描述

2、若想实现 QQ 登录,需要成为 QQ 互联的开发者,审核通过才可实现。<申请链接>

3、Django准备:

3.0、新建子应用apps/oauth,并注册到配置文件settings

进入 apps 目录下:
cd apps
调用命令生成 oauth 子应用:
python …/manage.py startapp oauth
(或者通过 django-admin startapp oauth 来生成)

3.1. 定义模型类基类

  • 在Django工程找一个合适的地方,新建文件models.py定义模型基类,以后只要是继承该基类的模型类,都会有这两个字段。
from django.db import models

class BaseModel(models.Model):
    """为模型类补充字段"""

    # auto_now_add=True:表面当创建模型类对象的时候,会创建该字段的值
    create_time = models.DateTimeField(auto_now_add=True,  verbose_name="创建时间")
   
    # auto_now=True:表面当更新模型类对象的时候,会更新该字段的值
    update_time = models.DateTimeField(auto_now=True, verbose_name="更新时间")

    class Meta:
        # 说明是抽象模型类(抽象模型类不会创建表)
        abstract = True
  • 编辑文件apps/oauth/models.py新建模型类如
from django.db import models
from meiduo_mall.utils.models import BaseModel

class OAuthQQUser(BaseModel):
    """
    功能:把qq用户 和 商城用户进行绑定
    """

    # user 是个外键, 关联对应的用户 —— 美多商城用户
    user = models.ForeignKey('users.User',  on_delete=models.CASCADE, verbose_name='用户')

    # qq 布的用户身份id —— 用户使用qq扫码登陆之后,qq官方颁发的用户身份id
    openid = models.CharField(max_length=64,  verbose_name='openid', db_index=True)

    class Meta:
        db_table = 'tb_oauth_qq'

迁移建表(中间表)

生成迁移文件:
python manage.py makemigrations oauth
进行数据迁移:
python manage.py migrate oauth

4、QQLoginTool工具

该工具封装了对接 QQ 互联的请求操作.
可用于快速实现 QQ 登录的一种工具包.

安装:pip install QQLoginTool -i https://pypi.tuna.tsinghua.edu.cn/simple

使用步骤:

  • 1.导包
# 使用时, 需要导入该包:
# 我们可以从下载的 QQLoginTool 中导入 OAuthQQ:
from QQLoginTool.QQtool import OAuthQQ
  • 2.初始化 OAuthQQ 对象
# 创建对象
# 创建对象的时候, 需要传递四个参数: 
oauth = OAuthQQ(client_id=settings.QQ_CLIENT_ID,   # 在 QQ 互联申请到的客户端 id: app_id
                client_secret=settings.QQ_CLIENT_SECRET,   # 我们申请的客户端秘钥:app_key
                redirect_uri=settings.QQ_REDIRECT_URI,   # 我们申请时添加的: 登录成功后回调的路径
                state=None  # state是用户完成整个登陆流程之后,跳转到的路径

在这里插入图片描述

  1. 对象提供的方法一: 获取 QQ 地址.
    获取 QQ 登录扫码页面,扫码后得到 Authorization Code( 特许码 )
# 调用对象的 get_qq_url() 函数, 获取对应的扫码页面: 
login_url = oauth.get_qq_url()
  1. 对象提供的方法二: 获取 QQ 的 access_token.
# 调用对象的方法, 根据 code 获取 access_token: 
access_token = oauth.get_access_token(code)
  1. 再获取 OpenID
# 调用对象的方法, 根据 access_token 获取 openid: 
openid = oauth.get_open_id(access_token)

5、封装加密openID的工具

我们应该对openid 敏感数据加解密 ——使用itsdangerous里面的TimedJSONWebSignatureSerializer可以生成带有有效期的token

安装依赖包

pip install itsdangerous

在Django工程的合适位置新建secert.py文件进行封装

"""
封装加密和解密openid的接口
"""
from itsdangerous import TimedJSONWebSignatureSerializer
from django.conf import settings
# 原则上一个类的封装分为2个部分:
# 1、操作所需要的属性
# 2、函数方法
class SecretOauth(object):

    def __init__(self):
        # 由于加密和解密都需要TimedJSONWebSignatureSerializer对象
        # 就可以把该对象封装到属性中
        self.serializer = TimedJSONWebSignatureSerializer(
            secret_key=settings.SECRET_KEY, # 使用Django工程初始化的密钥
            expires_in=24 * 15 * 60
        )

    # (1)、加密
    def dumps(self, content_dict):
        """
        功能:加密openid
        参数:content_dict = {"open_id": "fnhewrbfhreubvw"}
        :return: 返回加密后的access_token
        """
        access_token = self.serializer.dumps(content_dict)
        return access_token.decode() # 返回的是一个字符串

    # (2)、解密
    def loads(self, access_token):
        """
        功能:解密access_token
        参数:access_token = "fewbfherbgerg.regwertgt.wgtrwgtfgrew"
        :return: 解密后的字典
        """
        try:
            data = self.serializer.loads(access_token)
        except Exception as e:
            # 解密/校验失败 —— 要么伪造了要么过了有效期
            return None

        # 校验成功返回解密后的字典{"open_id": "fnhewrbfhreubvw"}
        return data

6、视图接口源码如下:

import json
from QQLoginTool.QQtool import OAuthQQ
from django.conf import settings
from django.contrib.auth import login
from django.http import JsonResponse
from django.views import View
from apps.users.models import User
from apps.oauth.models import OAuthQQUser
from super_mall.utils.secret import SecretOauth


# qq登陆接口1:获取扫码页面url
class QQURLView(View):
    def get(self, request):

        next = request.GET.get('next')
		
		# 创建OAuthQQ一个对象oauth
        oauth = OAuthQQ(
            client_id=settings.QQ_CLIENT_ID,
            client_secret=settings.QQ_CLIENT_SECRET,
            redirect_uri=settings.QQ_REDIRECT_URI,
            state=next
        )

        # 获取扫码登陆页面的url
        log_url = oauth.get_qq_url()

        return JsonResponse({
            'code': 0,
            'errmsg': 'ok',
            'login_url': log_url
        })


class QQUserView(View):

	 # qq登陆接口2:验证code获取openid
    def get(self, request):
        # 1、提取参数
        # 此处的code,是用户扫码验证qq身份成功之后,qq给用户
        # 用户转而携带到美多后台来的
        code = request.GET.get('code')
        # 2、校验参数
        # 2.1、必要性校验
        if not code:
            return JsonResponse({
                'code': 400,
                'errmsg': '缺少参数'
            }, status=400)

        # 3、数据/业务处理:验证code获取openid
        # 3.1、实例化qq认证对象
        oauth = OAuthQQ(
            client_id=settings.QQ_CLIENT_ID,
            client_secret=settings.QQ_CLIENT_SECRET,
            redirect_uri=settings.QQ_REDIRECT_URI,
            state='/'
        )

        try:
            # 3.2、调用接口获取access_token
            access_token = oauth.get_access_token(code)
            # 3.3、调用接口获取openid
            openid = oauth.get_open_id(access_token)
        except Exception as e:
            # 表示验证用户code失败
            return JsonResponse({
                'code': 400,
                'errmsg': 'qq身份验证失败'
            }, status=400)

        # 代码走到此处,表明,美多后台已经成功获取了用户的openid
        # 3.4、判读用户的"美多商城账号"和"openid"是否绑定
        try:
            oauth_user = OAuthQQUser.objects.get(openid=openid)
        except OAuthQQUser.DoesNotExist as e:

            # 3.4.1、用户没有绑定qq
            # 把openid加密返回给用户,由用户在页面中数据账号和密码,再进一步调用接口3来进行绑定操作
            access_token = SecretOauth().dumps({
                'openid': openid
            })
            return JsonResponse({
                'code': 400,  # 根据接口约定,自定义返回的code为400表示未绑定,前端就会跳转到绑定页面
                'errmsg': 'ok',
                'access_token': access_token
            })

        else:
            # 3.4.2、用户绑定过qq
            login(request, oauth_user.user)
            response = JsonResponse({
                'code': 0,
                'errmsg': 'ok'
            })
            response.set_cookie(
                'username',
                oauth_user.user.username,
                max_age=24 * 3600 * 14
            )
            return response
	
    # qq登陆接口3:把用户的商城账号 和 用户的qq身份(openid) 进行绑定
    def post(self, request):

        # 提取参数
        data = json.loads(request.body.decode())
        mobile = data.get('mobile')
        password = data.get('password')
        sms_code = data.get('sms_code')
        access_token = data.get('access_token')

        # 校验
        if not all([mobile, password, sms_code, access_token]):
            return JsonResponse({
                'code': 400,
                'errmsg': '参数缺少'
            }, status=400)

        # 手机号,密码,短信格式校验,略、
        
        token_dict = SecretOauth().loads(access_token)
        if token_dict is None:
            return JsonResponse({
                'code': 400,
                'errmsg': '无效的qq身份'
            }, status=400)
        openid = token_dict.get('openid')

        try:
            # 根据手机号查找用户,
            user = User.objects.get(mobile=mobile)

        except User.DoesNotExist as e:
            # 没找到则新建并绑定
            user = User.objects.create_user(
                username=mobile,
                mobile=mobile,
                password=password
            )
        else:
            # 找到则直接绑定
            # 先校验密码再绑定
            if not user.check_password(password):
                return JsonResponse({'code': 400, 'errmsg': '密码错误!'}, status=400)

        # 绑定(在中间表插入一条数据)
        OAuthQQUser.objects.create(
            user=user,
            openid=openid
        )
        login(request, user)

        response = JsonResponse({'code': 0, 'errmsg': 'ok'})
        response.set_cookie(
            'username',
            user.username,
            max_age=24*14*3600
        )
        return response
  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值