下面,我将以QQ第三方登录为例进行讲解,其它类型的第三方登录都是类似的流程。
1、QQ互联开发平台申请成为开发者
若想实现QQ登录,需要成为QQ互联的开发者,审核通过了才可以实现。
2、创建应用
成为QQ互联开发者之后,还需创建应用,获取本项目对应与QQ互联的应用ID。
3、根据文档进行开发
3.1、准备工作
QQ登录参数
我们申请的客户端id
QQ_CLIENT_ID
我们申请的客户端秘钥
QQ_CLIENT_SECRET
我们申请时添加的:登录成功后的回调路径
QQ_REDIRECT_URI
3.2、放置 QQ登录的图标(目的:让我们点击QQ图标实现第三方第三方登录)
3.3、根据oauth2.0 来获取code和token
对于应用而言,需要进行两步:
- 获取Authorization Code;
- 通过Authorization Code获取Access Token
3.4、通过token换取openid
openid是此网站上唯一对应用户身份的标识,网站可将此ID进行存储便于用户下次登录时辨识其身份,或将其与用户在网站上的原有账号进行绑定。
最终需要保存openid,所以需要去定义一个模型来保存。
定义QQ登录模型类
创建一个新的应用oauth,用来实现QQ第三方认证登录
在oauth/models.py中定义QQ身份(openid)和用户模型类User的关联关系
from django.db import models
from elephantmall.utils.models import BaseModel
# Create your models here.
class OAuthQQUser(BaseModel):
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 = 'tb_oauth_qq'
verbose_name = "QQ登录用户数据"
verbose_name_plural = verbose_name
QQ登录工具QQloginTool
1、QQLoginTool介绍
该工具封装了对接QQ互联的请求操作
可用于快速实现QQ登录的一种工具包
2、QQLoginTool安装
pip3 install QQLoginTool
3、QQLoginTool使用说明
- 导入
from QQLoginTool.QQtool import OAuthQQ
- 初始化OAuthQQ对象
qq = OAuthQQ(client_id=dev.QQ_CLIENT_ID,client_secret=dev.QQ_CLIENT_SECRET,redirect_uri=dev.QQ_REDIRECT_URI,state='xxxx')
- 获取QQ登录扫码页面,扫码后得到Authorization Code
qq_login_url = qq.get_qq_url()
- 通过Authorization Code 获取 Access Token
token = qq.get_access_token(code)
- 通过Access Token 获取 OpenID
openid = qq.get_open_id(token)
当点击QQ登录的图标,触发url链接跳转
在视图中生成第三方登录的链接地址并返回
当扫码并确认登录之后,会已GET的形式跳转到settings.py配置文件中的QQ_REDIRECT_URI并携带code和state两个参数信息
接下来,url会触发另外一个视图类,通过code获取token,通过token获取openid
接下来是openid是否绑定用户的处理逻辑
如上代码所示,通过查询QQ登录模型表,如果有记录,执行else分支,实现状态保持、重定向、设置cookie
如果没有记录,加密OpenID,携带加密后的信息,跳转到绑定页面oauth_callback.html
绑定用户的最终需求:绑定商城账号和QQ的OpenID
点击"保存"按钮,前端需要提交:手机号、密码、手机验证码、OpenID
后端业务逻辑:
接收请求
获取请求参数 openid
根据手机号进行用户信息的查询
查询到手机号已经注册。判断密码是否正确。就可以直接绑定用户和openid信息
查询到手机号没有注册。我们就创建一个user信息,然后在绑定
完成状态保持
返回响应
# 提取数据
mobile = request.POST.get("mobile")
password = request.POST.get("password")
sms_code = request.POST.get("sms_code")
access_token = request.POST.get("access_token")
state = request.GET.get("state")
# 校验数据
# 检查参数是否齐全
if not all([mobile, password, sms_code, access_token]):
return HttpResponseForbidden("缺少必须的数据")
# 判断手机号格式是否正确
if not re.match(r"^1[345789]\d{9}$", mobile):
return HttpResponseForbidden("手机号码格式错误")
# 判断密码格式是否正确
if not re.match(r"^[0-9A-Za-z]{8,20}$", password):
return HttpResponseForbidden("密码格式错误")
# 判断短信验证码是否一致
conn = get_redis_connection("sms_code")
sms_code_server = conn.get(mobile)
if sms_code_server is None:
return render(request, 'oauth_callback.html', {'sms_code_errmsg': '短信验证码已失效'})
if sms_code != sms_code_server.decode():
return render(request, 'oauth_callback.html', {'sms_code_errmsg': '短信验证码错误'})
# 判断openid是否有效
open_id = Signer.unsign(access_token)
if open_id is None:
return render(request, 'oauth_callback.html', {'openid_errmsg': '无效的openid'})
else:
open_id = open_id["open_id"]
# 处理逻辑
try:
# 查询手机号用户是否存在
user = User.objects.get(mobile=mobile)
except User.DoesNotExist:
# 用户不存在, 创建用户
user = User.objects.create_user(username=mobile, mobile=mobile, password=password)
else:
# 用户存在,检查用户密码是否正确
if not user.check_password(password):
return render(request, 'oauth_callback.html', {'account_errmsg': '用户名或密码错误'})
# 绑定用户
try:
OAuthQQUser.objects.create(user=user, openid=open_id)
except DatabaseError:
return render(request, 'oauth_callback.html', {'qq_login_errmsg': 'QQ登录失败'})
# 登陆状态保持
login(request, user)
response = redirect(state)
# 登陆时把用户名写入 cookie
response.set_cookie("username", user.username, expires=constants.USERNAME_COOKIE_EXPIRES)
# 响应
return response
openid 我们传递给了前端。这个敏感数据,我们最好加密处理一下。
自定义Signer类,封装itsdangerous工具的加密和解密方法
from django.conf import settings
from itsdangerous import BadSignature, TimedJSONWebSignatureSerializer
from woniu_mall.utils import constants
class Signer(object):
serializer = TimedJSONWebSignatureSerializer(settings.SECRET_KEY, expires_in=constants.OPENID_SIGN_EXPIRES)
@classmethod
def sign(cls, obj):
"""
对python字典先进行json序列化,再对序列化结果进行签名
:param obj:
:return: 签名后的字符串
"""
token = cls.serializer.dumps(obj)
return token.decode()
@classmethod
def unsign(cls, s):
"""
对传入的字符串验证签名, 验证成功返回字符串中的被签名的数据对应的python字典
:param s: 要验证的签名字符串
:return: python字典
"""
try:
obj = cls.serializer.loads(s)
except BadSignature:
obj = None
return obj
总的概括:
QQ第三方登录流程
1、QQ互联开发平台申请成为开发者
2、创建应用,获取QQ_CLIENT_ID、QQ_CLIENT_SECRET、QQ_REDIRECT_URI三个QQ登录的参数
3、前端放置QQ登录的图标
4、后端定义QQ登录模型类
5、当点击QQ登录的图标之后,触发链接,跳转到后端逻辑,后端实现生成第三方登录的链接地址并返回
6、用户在弹出的页面中扫码,确认登记之后,会生成一个携带code的链接,该链接会触发后端的逻辑,后端根据该code获取token,再根据token获取openid,在查询openid看有没有记录,如果没有,携带解密后的openid跳转到绑定手机号的页面,如果有,保持登录状态设置cookie,跳转到指定页面
7、在绑定页面,填写信息校验之后,点击保存或确认,会将openid、手机号、密码、手机验证码信息发送给后端,后端根据手机号和密码,查询数据库,没有则插入新数据,有则校验手机号和密码是否配对。然后在QQ登录模型类中创建记录。