django web网站实现第三方QQ登录
第一步:成为开发者
成为QQ互联的开发者,审核通过才可实现;审核通过后要创建应用,即获取本项目对应与QQ互联的应用ID。
QQ登录开发文档:http://wiki.connect.qq.com/准备工作_oauth2-0
第二步:配置参数
成为开发者之后,再django的setting.py文件中配置QQ登录参数
QQ_CLIENT_ID = '' # appid
QQ_CLIENT_SECRET = '' # appkey
QQ_REDIRECT_URI = 'http://127.0.0.1:8080/XXX.html' # 跳转url
然后创建一个新的应用oauth,用来实现QQ第三方认证登录,并注册到INSTALLED_APPS = ['oauth.apps.oauthConfig',]
设置总路由前缀为 oauth/
在utils/models.py文件中创建模型类基类,用于增加数据新建时间和更新时间
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 utils.models import BaseModel
class OAuthQQUser(BaseModel):
# 定义QQ身份(openid)与用户模型类User的关联关系
user = models.ForeignKey('users.User', on_delete=models.CASCADE, verbose_name='用户')
openid = models.CharField(max_length=64, verbose_name='openid', db_index=True)
class Meta:
db_table = 'oauth_qq'
verbose_name = 'QQ用户数据'
verbose_name_plural = verbose_name
第三步:QQ登录扫码页面
提供QQ登录扫码页面网址https://graph.qq.com/oauth2.0/authorize?response_type=code&client_id=xxx&redirect_uri=xxx&state=xxx
pip install QQLoginTool 安装qq登录工具
这个集成了QQ辅助类,可以让自己少些写代码,方便调用,这里贴一点它的源代码
from django.conf import settings
from urllib.parse import urlencode, parse_qs
import json
import requests
class OAuthQQ(object):
"""
QQ认证辅助工具类
"""
def __init__(self, client_id=None, client_secret=None, redirect_uri=None, state=None):
self.client_id = client_id
self.client_secret = client_secret
self.redirect_uri = redirect_uri
self.state = state # 用于保存登录成功后的跳转页面路径
def get_qq_url(self):
# QQ登录url参数组建
data_dict = {
'response_type': 'code',
'client_id': self.client_id,
'redirect_uri': self.redirect_uri,
'state': self.state
}
# 构建url
qq_url = 'https://graph.qq.com/oauth2.0/authorize?' + urlencode(data_dict)
return qq_url
# 获取access_token值
def get_access_token(self, code):
# 构建参数数据
data_dict = {
'grant_type': 'authorization_code',
'client_id': self.client_id,
'client_secret': self.client_secret,
'redirect_uri': self.redirect_uri,
'code': code
}
# 构建url
access_url = 'https://graph.qq.com/oauth2.0/token?' + urlencode(data_dict)
# 发送请求
try:
response = requests.get(access_url)
# 提取数据
# access_token=FE04************************CCE2&expires_in=7776000&refresh_token=88E4************************BE14
data = response.text
# 转化为字典
data = parse_qs(data)
except:
raise Exception('qq请求失败')
# 提取access_token
access_token = data.get('access_token', None)
if not access_token:
raise Exception('access_token获取失败')
return access_token[0]
# 获取open_id值
def get_open_id(self, access_token):
# 构建请求url
url = 'https://graph.qq.com/oauth2.0/me?access_token=' + access_token
# 发送请求
try:
response = requests.get(url)
# 提取数据
# callback( {"client_id":"YOUR_APPID","openid":"YOUR_OPENID"} );
# code=asdasd&msg=asjdhui 错误的时候返回的结果
data = response.text
data = data[10:-3]
except:
raise Exception('qq请求失败')
# 转化为字典
try:
data_dict = json.loads(data)
# 获取openid
openid = data_dict.get('openid')
except:
raise Exception('openid获取失败')
return openid
pip install itsdangerous
使用TimedJSONWebSignatureSerializer可以生成带有有效期的token
from itsdangerous import TimedJSONWebSignatureSerializer as TJS
from django.conf import settings
# tjs= TJS(秘钥, 有效期秒)
tjs= TJS(settings.SECRET_KEY, 300)
# tjs.dumps(数据), 返回bytes类型
token = tjs.dumps({'mobile': '18512345678'})
token = token.decode()
# 检验token
# 验证失败,会抛出itsdangerous.BadData异常
tjs= TJS(settings.SECRET_KEY, 300)
try:
data = tjs.loads(token)
except BadData:
return None
第四步:获取QQ用户OpenID
扫码成功后,要准备一个回调页面,这里就不做详细的讲解,下面就是获取QQ用户OpenID,
在QQ将用户重定向到此网页的时候,重定向的网址会携带QQ提供的code参数,用于获取用户信息使用,我们需要将这个code参数发送给后端,在后端中使用code参数向QQ请求用户的身份信息,并查询与该QQ用户绑定的用户。
from QQLoginTool.QQtool import OAuthQQ
from rest_framework import status
from rest_framework.views import APIView
from rest_framework.response import Response
from django.conf import settings
from rest_framework_jwt.settings import api_settings
from oauth.models import OAuthQQUser
from itsdangerous import TimedJSONWebSignatureSerializer as TJS
from oauth.serializers import OauthSerializers
class QQAuthUserView(APIView):
"""用户扫码登录的回调处理"""
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(client_id=settings.QQ_CLIENT_ID,
client_secret=settings.QQ_CLIENT_SECRET, redirect_uri=settings.QQ_REDIRECT_URI)
try:
# 使用code向QQ服务器请求access_token
access_token = oauth.get_access_token(code)
# 使用access_token向QQ服务器请求openid
openid = oauth.get_open_id(access_token)
except Exception:
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
# 为了能够在后续的绑定用户操作中前端可以使用openid,在这里将openid签名后响应给前端
tjs = TJS(settings.SECRET_KEY, 300) # 使用TimedJSONWebSignatureSerializer生成带有有效期的token
open_id = tjs.dumps({'openid': openid}).decode()
return Response({'access_token': open_id})
else:
# 如果openid已绑定用户,直接生成JWT token,并返回
jwt_payload_handler = api_settings.JWT_PAYLOAD_HANDLER
jwt_encode_handler = api_settings.JWT_ENCODE_HANDLER
# 获取oauth_user关联的user
user = oauth_user.user
payload = jwt_payload_handler(user)
token = jwt_encode_handler(payload)
response = Response({
'token': token,
'user_id': user.id,
'username': user.username
})
return response
def post(self, request):
# 1、获取前端数据
data = request.data
# 2、验证数据
ser = OauthSerializers(data=data)
ser.is_valid()
print(ser.errors)
# 3、绑定保存数据
ser.save()
# 4、返回结果
return Response(ser.data)
根据openid查询用户,如果能够查询到用户,就直接生成状态保持信息,登录到网站
如果不能查询到用户,就直接将OpenID序列化并返回给前端,用于后续的绑定网站用户操作。
第五步:openid绑定用户
用户需要填写手机号、密码、短信验证码
如果用户未在此网站注册过,则会将手机号作为用户名为用户创建一个网站账户,并绑定用户
如果用户已在此网站注册过,则检验密码后直接绑定用户