第三方登陆--QQ登陆

QQ登录,亦即我们所说的第三方登录,是指用户可以不在本项目中输入密码,而直接通过第三方的验证,成功登录网站/移动端。

在进行QQ登陆的实现之前,我们需要根据QQ互联的要求,进行相关的操作:
1.成为开发者 参考链接 : http://wiki.connect.qq.com/成为开发者
2.应用创建 参考链接:http://wiki.connect.qq.com/__trashed-2


假设条件:

㈠:我的网站的域名是: http://iloveqq.com

㈡:用户在访问:http://iloveqq.com/like 界面时需要进行登陆

㈢:QQ认证的相关函数,我们放在一个类中,作为方法,在使用的时候,可以通过对象调用方法的方式来进行操作:

class OAuthQQ(object):
    """QQ认证辅助工具"""
    def __init__(self, client_id=None, redirect_uri=None, state=None, client_secret=None):
    #  settings中有client_id,redirect_uri,client_secret的值,如果在创建对象的时候不传参,默认使用配置中的;
    
        self.client_id = client_id or settings.QQ_CLIENT_ID
        self.redirect_uri = redirect_uri or settings.QQ_REDIRECT_URI
        self.state = state or settings.QQ_STATE
        self.client_secret = client_secret or settings.QQ_CLIENT_SECRET

㈣: urllib使用说明
在后端接口中,我们需要向QQ服务器发送请求,查询用户的QQ信息,Python提供了标准模块urllib可以帮助我们发送http请求。
1.将query字典转换为url路径中的查询字符串

urllib.parse.urlencode(query)

2.将qs查询字符串格式数据转换为python的字典

urllib.parse.parse_qs(qs)

3.发送http请求,如果data为None,发送GET请求,如果data不为None,发送POST请求

urllib.request.urlopen(url, data=None)

返回response响应对象,可以通过read()读取响应体数据,需要注意读取出的响应体数据为bytes类型


现在来说一下,整个QQ登陆的过程:

第三方登陆--QQ登陆_第1张图片

Step1:获取Authorization Code

1.用户进行qq登陆 - - QQ服务器生成code

用户想进入like.html界面进行查阅,但是网站跳出登陆界面,显示必须先登录才能继续查看;然后,用户点击qq登陆的图标,然后出现这样的图片:
第三方登陆--QQ登陆_第2张图片

用户点击qq登陆的时候,我们需要将登陆qq的url返回给用户,这个url必须是可以进行qq登陆,满足qq互联的要求:

如下图所示,我们需要请求的网址是:https://graph.qq.com/oauth2.0/authorize; 同时,我们还需要将response_type,client_id, redirect_uri,state这几个参数放在链接后面携带过去
第三方登陆--QQ登陆_第3张图片

后端生成登陆地址(生成login_url的方式如下):

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

注意,在qq登陆时序图中,我们需要获取到用户想要访问的界面,也就是本案例中的http://iloveqq.com/like 链接,此时前端是通过next=http://iloveqq.com/like 传给后端的,然后后端在生成url时,通过获取next参数并传入get_qq_login_url(state)这个函数中,给state赋值:

login_url = get_qq_login_url(next)

然后再将login_url返回给前端,此时用户通过扫描二维码,进行授权登陆,用户登陆成功。

2.登陆成功,服务器获取code

在用户授权确认登陆的时候,QQ会将返回回调页面 redirect_uri.html,当然,此时,QQ返回的参数中,还携带了codestate参数(上面传入的next参数的内容);

此时用户需要访问我们的回调页面 redirect_uri.html,当然请求的时候,除了回调页面,还会携带参数code和state:
访问链接比如: GET /oauth/qq/user/?code=xxx

此时,后端可以通过查询字符串的参数获取方式,获取到code数据

Step2:通过Authorization Code获取Access Token

3.我的服务器和QQ服务器的纠缠-- Access Token

此时,后端是已经获取到梦寐以求的code数据了,现在开始就是我们自己的服务器和QQ服务器之间的数据获取了。

1.根据code,向QQ服务器发送请求获取access_token:

    def get_access_token(self, code):
        """获取access_token值"""
        params = {
            'grant_type': 'authorization_code',
            'client_id': self.client_id,
            'client_secret': self.client_secret,
            'code': code,
            'redirect_uri': self.redirect_uri
        }

        url = 'https://graph.qq.com/oauth2.0/token?' + urlencode(params)
        
        #向qq服务器发送GET请求,链接是url
        response = urlopen(url)
        
        #QQ返回的响应数据通过read()读取,并且需要进行解码
        resp_data = response.read().decode()
        
        # 将接收的resp_data查询字符串格式数据转换为python的字典
        resp_dict = parse_qs(resp_data)
        
        # 通过字典取值的方式,获取access_token的值
        access_token = resp_dict.get('access_token')
	
		# 获取到的access_token是一个列表,类似['2334tf2rwfreytrhh'],所以需要通过索引access_token[0]取出字符串数据
        return access_token[0]

第三方登陆--QQ登陆_第4张图片

Step3:通过Access Token 获取获取用户OpenID_OAuth2.0

4.我的服务器和QQ服务器的纠缠-- OpenID

第三方登陆--QQ登陆_第5张图片

上一步已经通过code获取到Access Token的值,现在可以通过Access Token获取openid的值了:

    def get_openid(self, access_token):
        """openid的获取"""
        url = 'https://graph.qq.com/oauth2.0/me?access_token=' + access_token
        
		# 访问QQ服务器
        response = urlopen(url)
        resp_data = response.read().decode()  # 字符串
        
        try:
            # 返回的数据 ‘callback( {"client_id":"YOUR_APPID","openid":"YOUR_OPENID"} )\n’  取出数据,并将字符串转为字典
            data = json.loads(resp_data[10:-4])  # 也可以使用正则取出数据
       
        except Exception:
        	# 接口调用有错误时,会返回code和msg字段
            data = parse_qs(resp_data)
            logger.error('code=%s msg=%s' % (data.get('code'), data.get('msg')))
            raise QQAPIError

        openid = data.get('openid', None)
        return openid

每一个openid会对应一个QQ号,在后端获取到openid之后,可以根据后端的逻辑进行相关处理。


完整的参考代码----》这里使用类的方式进行封装

class OAuthQQ(object):
    """QQ认证辅助工具"""
    def __init__(self, client_id=None, redirect_uri=None, state=None, client_secret=None):
        self.client_id = client_id or settings.QQ_CLIENT_ID
        self.redirect_uri = redirect_uri or settings.QQ_REDIRECT_URI
        self.state = state or settings.QQ_STATE
        self.client_secret = client_secret or settings.QQ_CLIENT_SECRET

    def get_qq_login_url(self):
        """关于QQ登陆获取url"""
        """Step1:获取Authorization Code
            请求地址:
            PC网站:https://graph.qq.com/oauth2.0/authorize
            请求方法:GET
            参数	是否必须	含义
            response_type	此值固定为“code”。
            client_id	appid。
            redirect_uri
            state
        """

        params = {
            'response_type': 'code',
            'client_id': self.client_id,
            'redirect_uri': self.redirect_uri,
            'state': self.state
        }

        """urllib.parse.urlencode(query) 将query字典转换为url路径中的查询字符串"""
        
        # 字符串url的拼接
        url = 'https://graph.qq.com/oauth2.0/authorize?' + urlencode(params)

        return url


    def get_access_token(self, code):
        """获取access_token值"""

        """请求地址:
            PC网站:https://graph.qq.com/oauth2.0/token
            请求方法:GET
            grant_type	在本步骤中,此值为“authorization_code”。
            client_id	appid。
            client_secret	appkey。
            code	authorization code。
            redirect_uri	
            """
            
        params = {
            'grant_type': 'authorization_code',
            'client_id': self.client_id,
            'client_secret': self.client_secret,
            'code': code,
            'redirect_uri': self.redirect_uri
        }

        url = 'https://graph.qq.com/oauth2.0/token?' + urlencode(params)

        """urllib.request.urlopen(url, data=None)
    发送http请求,如果data为None,发送GET请求,如果data不为None,发送POST请求
    返回response响应对象,可以通过read()读取响应体数据,需要注意读取出的响应体数据为bytes类型"""

        response = urlopen(url)
        resp_data = response.read().decode()

        """urllib.parse.parse_qs(qs)将qs查询字符串格式数据转换为python的字典"""
        
        resp_dict = parse_qs(resp_data)
        access_token = resp_dict.get('access_token')
        
        return access_token[0]



    def get_openid(self, access_token):
        """openid的获取"""
        """1 请求地址
            PC网站:https://graph.qq.com/oauth2.0/me
            2 请求方法
            GET
            3 请求参数
            access_token
            """
            
        url = 'https://graph.qq.com/oauth2.0/me?access_token=' + access_token

        response = urlopen(url)
        resp_data = response.read().decode()  # 字符串

        try:
            # 返回的数据 ‘callback( {"client_id":"YOUR_APPID","openid":"YOUR_OPENID"} )\n’ 取出数据,并将字符串转为字典
            data = json.loads(resp_data[10:-4])
            print('data:%s' % data)
        except Exception:
            data = parse_qs(resp_data)
            logger.error('code=%s msg=%s' % (data.get('code'), data.get('msg')))
            raise QQAPIError

        openid = data.get('openid', None)
        
        return openid

你可能感兴趣的:(QQ登陆)