QQ 第三方登录(Django)

张开发
2026/4/6 1:31:29 15 分钟阅读

分享文章

QQ 第三方登录(Django)
QQ 第三方登录Django本篇教程完全面向 Django 开发者从 QQ 互联原理、三端交互流程到完整代码实现一步到位新手可直接复制粘贴跟着操作避开所有常见踩坑点兼顾教学和实战需求。一、核心原理与三端交互流程1.1 核心角色用户发起登录请求的终端用户浏览器/客户端Django 后端我们自己的服务负责接收 QQ 回调、处理凭证交换、完成登录逻辑QQ 第三方服务提供授权、凭证发放、用户信息查询的服务QQ 互联平台1.2 核心凭证说明code临时授权码用户授权后 QQ 返回给 Django 后端单次有效、时效10分钟仅用于换取access_tokenaccess_token访问令牌用于向 QQ 服务请求用户资源如昵称、头像时效较长openid用户在当前 QQ 应用中的唯一身份标识用于绑定 Django 本地用户终身唯一同一用户在同一 QQ 应用下 openid 不变1.3 三端交互流程图二、前置准备QQ 互联平台配置在写代码前必须先在 QQ 互联平台完成配置获取核心参数APP_ID和APP_KEY步骤如下访问 http://connect.qq.com/intro/login/注册开发者账号需实名认证登录后进入「应用管理」→「创建应用」选择「网站应用」填写应用基本信息网站名称、域名、简介等提交审核审核约1-2个工作日个人开发者可正常通过审核通过后进入应用详情页获取APP ID和APP Key后续配置用配置「回调地址」在应用详情→「接口设置」中添加回调地址必须与 Django 代码中配置一致示例http://127.0.0.1:8000/user/qq/callback/开启「获取用户信息」接口权限默认开启若未开启需手动开启。⚠️ 注意本地开发时回调地址可使用127.0.0.1上线后需替换为自己的域名如https://www.xxx.com/user/qq/callback/。三、Django 项目环境准备3.1 安装依赖pip install djangorestframework #django的前后端分离拓展 pip install QQLoginTool #qq登录的SDK四、Django 核心配置修改项目settings.py添加 QQ 登录相关配置以及注册新建的user应用。# settings.py INSTALLED_APPS [ django.contrib.admin, django.contrib.auth, django.contrib.contenttypes, django.contrib.sessions, django.contrib.messages, django.contrib.staticfiles, oauth, # 注册用户应用 ] # QQ 第三方登录配置替换为你自己的 APP_ID 和 APP_KEY # QQ登录参数 QQ_CLIENT_ID #APP_ID QQ_CLIENT_SECRET #APP_KEY QQ_REDIRECT_URI http://localhost/oauth_callback.html # 与QQ互联配置的回调地址一致五、完整代码实现核心代码集中在oauth/views.py处理登录逻辑、回调逻辑、oauth/urls.py配置路由、oauth/utils对openid的加密解密逻辑蹲、oauth/serializers(数据格式的转换)。5.0 工具类utils.pyfrom itsdangerous import TimedSerializer as TJWSSerializer, BadData from django.conf import settings def generate_save_user_token(openid): 对openid进行加密 #1 创建序列化器 serializer TJWSSerializer(settings.SECRET_KEY) #2 对openid进行加密dumps加密后的数据是bytes类型 data {openid: openid} token serializer.dumps(data) #3 把加载后的openid返回 return token.decode() def check_save_user_token(access_token): 传入加密的openid进行解密并返回 # 创建加密的序列化器对象secret_key serializer TJWSSerializer(settings.SECRET_KEY) # 调用loads方法进行解密 try: data serializer.loads(access_token) except BadData: return None else: return data.get(openid)5.1序列化器类serializers.pyfrom django_redis import get_redis_connection from rest_framework import serializers from oauth.models import OAuthQQUser from oauth.utils import check_save_user_token from users.models import User class QQAuthUserSerializer(serializers.Serializer): openid绑定用户序列化器 #mobile password sms_code Token mobile serializers.RegexField(label手机号,regexr^1[3-9]\d{9}$) password serializers.CharField(label确认密码, write_onlyTrue) sms_code serializers.CharField(label验证码, write_onlyTrue) access_token serializers.CharField(label加密后的openid) def validate(self, attrs): # 1.把加密后的openid取出来 加密 access_token attrs.get(access_token) openid check_save_user_token(access_token) if openid is None: raise serializers.ValidationError(openid无效) #将解密后的openid重新添加到attrs(以备后期create方法中绑定使用) attrs[openid] openid # 2.校验验证码 redis_conn get_redis_connection(verify_codes) # 连接我们settings中定义的verify_codes数据库 mobile attrs[mobile] real_sms_code redis_conn.get(sms_%s % mobile) # 想redis存储数据时都是以字条串进行存储的取出来后都是bytes类型【bytes】 if real_sms_code is None or attrs[sms_code] ! real_sms_code.decode(): raise serializers.ValidationError(验证码错误) # 3.拿手机号查询user表如果能查到说明手机号已注册再检验密码是否一致 try: user User.objects.get(mobilemobile) except: pass else: if user.check_password(attrs[password]) is False: raise serializers.ValidationError(密码错误) else: #如果用户存在且密码正确把当前user对象存储到反序列化大字典里以备后期绑定使用 attrs[user] user return attrs def create(self, validated_data): #1 获取validated_data中的user能取到说明用户存在 user validated_data.get(user) if user is None: #2 如果没取到user 新建用户 user User( username validated_data.get(mobile), mobilde validated_data.get(mobile) ) user.set_password(validated_data.get(password)) user.save() #3 openid与user绑定 OAuthQQUser.objects.create( openidvalidated_data.get(access_token), useruser, ) return user5.2 视图函数views.py包含两个核心视图QQOauthURLView拼接QQ登录URL、QQAuthUserView处理回调、凭证交换、登录逻辑。from rest_framework import status from rest_framework.views import APIView from QQLoginTool.QQtool import OAuthQQ from rest_framework.response import Response from django.conf import settings from .models import OAuthQQUser from carts.utils import merge_cart_cookie_to_redis from meiduo_mall.utils.exceptions import logger from rest_framework_simplejwt.tokens import RefreshToken from .serializers import QQAuthUserSerializer from .utils import generate_save_user_token # Create your views here. class QQOauthURLView(APIView): 拼接好qq登录的路由 def get(self, request): #1.拼接好前端传来的next参数记录用户从哪里来到qq页面 next request.query_params.get(next) if not next: next / #2 利用qq登录SDK (app_ID app_key 回调域名 记录来源)参数必传 oauth OAuthQQ(client_idsettings.QQ_CLIENT_ID, client_secretsettings.QQ_CLIENT_SECRET, redirect_urisettings.QQ_REDIRECT_URI, statenext) #3 创建QQ登录工具对象 # 调用SDK里的方法拼接好QQ登陆网址 login_url oauth.get_qq_url() return Response({login_url: login_url}) class QQAuthUserView(APIView): qq登陆成功后的回调处理 def get(self, request): #获取前端传入的code code request.query_params.get(code) if not code: return Response({message:确实code},statusstatus.HTTP_400_BAD_REQUEST) #创建qq登录工具对象 oauth OAuthQQ(client_idsettings.QQ_CLIENT_ID, client_secretsettings.QQ_CLIENT_SECRET, redirect_urisettings.QQ_REDIRECT_URI, statenext) try: #调用get_access_token(code)用code向qq服务器获取access_token access_token oauth.get_access_token(code) #调用get_open_id(access_token) 用access_token响应qq服务器获取open_id openid oauth.get_open_id(access_token) except Exception as e: logger.info(e) return Response({message:qq服务器不可用},statusstatus.HTTP_503_SERVICE_UNAVAILABLE) #查询数据库有无openid try: authQQUserModelOAuthQQUser.objects.get(openidopenid) except OAuthQQUser.DoesNotExist: #若没有则新建用户绑定openid前端暂存加密后的openid openid_encoded generate_save_user_token(openid) return Response({access_token:openid_encoded },statusstatus.HTTP_404_NOT_FOUND) else: #若有openid直接登陆成功给前端返回JWT状态保存信息 user authQQUserModel.user refresh RefreshToken.for_user(user) access_token refresh.access_token response Response({ access_token: str(access_token), refresh_token: str(refresh), username:user.username, user_id:user.id }) #创建响应对象 merge_cart_cookie_to_redis(request,user,response) return response def post(self, request): openid绑定用户接口 #创建序列化器进行反序列化 serializer QQAuthUserSerializer(datarequest.data) #调用is_valid方法校验 serializer.is_valid(raise_exceptionTrue) #调用序列化器的save方法 user serializer.save() #生成JWT状态保存token refresh RefreshToken.for_user(user) access_token str(refresh.access_token) #响应 return Response({ access_token: access_token, refresh_token: str(refresh), username:user.username, user_id:user.id })5.3 路由配置urls.py分别配置「QQ登录跳转路由」和「回调路由」修改oauth/urls.py若没有该文件新建一个。from django.urls import re_path from . import views urlpatterns [ # 拼接qq登录URL re_path(rqq/authorization/$, views.QQOauthURLView.as_view()), # qq登录成功后的回调处理 re_path(rqq/user/$, views.QQAuthUserView.as_view()), ]5.4 前端登录在前端资源login.js中添加 QQ 登录按钮,这里只展示绑定的请求。// qq登 录 qq_login: function () { var next this.get_query_string(next) || /; axios.get(this.host /oauth/qq/authorization/?next next, { responseType: json, withCredentials: true }) .then(response { location.href response.data.login_url; }) .catch(error { console.log(error.response.data); }) } } //回调页面以查询字符串方式携带code axios.get(this.host /oauth/qq/user/?code code, { responseType: json, withCredentials: true // 跨域允许携带cookie })六、登陆的整个逻辑

更多文章