admin管理员组

文章数量:1568305

一、什么是第三方登录
是指用户可以不在本项目中输入密码,而直接通过第三方的验证,成功登录本项目。
本文将以QQ登录为例进行详解第三方登录的过程。
二、实现QQ登录
1、准备工作
接入QQ登录前,网站需首先进行申请,获得对应的appid与appkey,以保证后续流程中可正确对网站与用户进行验证与授权。
(1)需要成为QQ互联的开发者,审核通过才可实现;
(2)成为QQ互联开发者后,还需创建应用,即获取本项目对应与QQ互联的应用ID;
appid:应用的唯一标识。在OAuth2.0认证过程中,appid的值即为 oauth_consumer_key的值。
appkey:appid对应的密钥,访问用户资源时用来验证应用的合法性。在OAuth2.0认证过程中,appkey的值即为oauth_consumer_secret的值。
2、QQ登录的开发流程

如上图所示:
(1)当用户点击前端界面上的QQ登录的图标时,便会向后端发起一次获取QQ登录的url。为什么不把url直接放置在前端呢?原因是与用户关联的登录地址是需要与后台的appid与appkey,很显然这两个字段是不能暴露出来的,否则会被他人滥用了。以下是QQ登录的地址及参数说明:
Step1:获取Authorization Code
请求地址:
PC网站:https://graph.qq/oauth2.0/authorize
请求方法:
GET
请求参数请包含如下内容:

代码实现:

import requests
from urllib.parse import urlencode
from django.conf import settings

class OAuthQQ(object):
    """
    QQ认证辅助工具类
    """
    def __init__(self, app_id=None, app_key=None, redirect_uri=None, state=None):
        self.app_id = app_id or settings.QQ_APP_ID
        self.app_key = app_key or settings.QQ_APP_KEY
        self.redirect_url = redirect_uri or settings.QQ_REDIRECT_URL
        self.state = state or '/'  # 用于保存登录成功后的跳转页面路径

    def get_auth_url(self):
        """
        获取qq登录的网址
        :return: url网址
        """
        params = {
            'response_type': 'code',
            'client_id': self.app_id,
            'redirect_uri': self.redirect_url,
            'state': self.state,
            'scope': 'get_user_info',
        }
        url = 'https://graph.qq/oauth2.0/authorize?' + urlencode(params)
     
        return url

(2) 获取到qq登录地址后,前端进一步做跳转,让用户在QQ登录平台做认证,这个过程,我们是无法操作的;如果用户成功登录并授权,则会跳转到指定的回调地址,并在redirect_uri地址后带上Authorization Code和原始的state值;如果用户在登录授权过程中取消登录流程,对于PC网站,登录页面直接关闭;对于WAP网站,同样跳转回指定的回调地址,并在redirect_uri地址后带上usercancel参数和原始的state值,其中usercancel值为非零。注意:此code会在10分钟内过期。
在用户授权登录成功后,就跳转到我们就设置好了回调地址redirect_uri,即返回了一个HTML的界面,在页面加载完成前,前端应把授权code返回到后端,后端才能发起下一步的请求:获取access_token
(3)通过Authorization Code获取Access Token
请求地址:
PC网站:https://graph.qq/oauth2.0/token
请求方法:
GET
请求参数:

代码实现:
在QQ认证辅助类中添加如下代码:

def get_access_token(self, code):
        """
        获取access_token
        :param code: qq提供的code
        :return: access_token
        """
        params = {
            'grant_type': 'authorization_code',
            'client_id': self.app_id,
            'client_secret': self.app_key,
            'code': code,
            'redirect_uri': self.redirect_url
        }
        response_data = requests.get(url,params=params).text
        data = parse_qs(response_data) # 将查询字符串转字典
        access_token = data.get('access_token', None)
        if not access_token:
            raise QQAPIError    # 自定义错误类
            
        return access_token[0]

如果成功返回,即可在返回包中获取到Access Token。 如:

access_token=FE04************************CCE2&expires_in=7776000&refresh_token=88E4************************BE14

接口调用有错误时,会返回code和msg字段,以url参数对的形式返回,value部分会进行url编码(UTF-8)。

以上方式获取到的access_token存在一定的有效期(3个月),因此QQ官方也提供了一个自动自动续期,避免要求用户再次授权的操作,提升用户体验。具体可以参考QQ授权的第三步,获取access_token:(可选)http://wiki.connect.qq/%E4%BD%BF%E7%94%A8authorization_code%E8%8E%B7%E5%8F%96access_token
(4)获取用户OpenID_OAuth2.0
本步骤的作用:
通过输入在上一步获取的Access Token,得到对应用户身份的OpenID。
OpenID是此网站上或应用中唯一对应用户身份的标识,网站或应用可将此ID进行存储,便于用户下次登录时辨识其身份,或将其与用户在网站上或应用中的原有账号进行绑定。
接口说明:

代码实现:
在QQ认证辅助类中添加如下代码:

def get_openid(self, access_token):
        """
        获取用户的openid
        :param access_token: qq提供的access_token
        :return: open_id
        """
        params = {
        	'access_token':access_token
        }
        url = 'https://graph.qq/oauth2.0/me
        response_data = requests.get(url,params=params)
        try:
            # 返回的数据 callback( {"client_id":"YOUR_APPID","openid":"YOUR_OPENID"} )\n;
            data = json.loads(response_data[10:-4]) #不知道为什么要返回这么反人类的参数。。
        except Exception:
            raise QQAPIError
        openid = data.get('openid', None)
        return openid

(5)根据openID获取用户基本信息

返回上面的接口便可以拿到用户的QQ名、个人签名、头像等等信息,此处的代码实现就省略不写了。。
至此,与QQ方面相关的操作已经全部完成了,接下来的是用户与项目服务器之间的操作了。
以下步骤也可以省略,因为用户使用第三方登录,就是希望不要去填写登录注册那些恶心的东西,再去绑定的话,会奔溃的吧…
三、将openId与user_id进行关联
1、首先,创建一张QQ用户表与user表进行关联,代码如下:

from django.db import models

class OAuthQQUser(model.Model):
    """
    QQ登录用户数据
    """
    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

2、判断用户是否绑定
查询数据库中是否有该用户的openID,如果有则说明已经登录并绑定过了,那么按照正常的登录步骤流程往下即可;如果没有,则让前端跳到附带access_token的用户绑定界面。那么用户提交绑定界面时,把手机号、昵称密码传到后端进行处理。代码如下:

根据手机号获取user
if not user: # 做此步判断是因为用户可能已经在服务器端注册过了
   # 用户不存在,则更新数据库中user的相关信息
   user = User.objects.create_user(
       username=validated_data['mobile'],
       password=validated_data['password'],
       mobile=validated_data['mobile'],
   )
# 关联QQ_openId
OAuthQQUser.objects.create(
   openid=openid
   user=user
)

本文标签: 单点第三方