分享web开发知识

注册/登录|最近发布|今日推荐

主页 IT知识网页技术软件开发前端开发代码编程运营维护技术分享教程案例
当前位置:首页 > 前端开发

如何通过第三方QQ登录网站首页

发布时间:2023-09-06 02:02责任编辑:傅花花关键词:暂无标签

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 makemigrationspython 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_qsfrom urllib.request import urlopenfrom itsdangerous import TimedJSONWebSignatureSerializer as Serializer, BadDatafrom django.conf import settingsimport jsonimport loggingfrom . import constantslogger = 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 serializersfrom .utils import OAuthQQfrom django_redis import get_redis_connectionfrom users.models import Userfrom .models import OAuthQQUserclass 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身份的处理流程图供参考

如何通过第三方QQ登录网站首页

原文地址:https://www.cnblogs.com/cl-python/p/9241317.html

知识推荐

我的编程学习网——分享web前端后端开发技术知识。 垃圾信息处理邮箱 tousu563@163.com 网站地图
icp备案号 闽ICP备2023006418号-8 不良信息举报平台 互联网安全管理备案 Copyright 2023 www.wodecom.cn All Rights Reserved