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类型
Step1:获取Authorization Code
用户想进入like.html界面进行查阅,但是网站跳出登陆界面,显示必须先登录才能继续查看;然后,用户点击qq登陆的图标,然后出现这样的图片:
用户点击qq登陆的时候,我们需要将登陆qq的url返回给用户,这个url必须是可以进行qq登陆,满足qq互联的要求:
如下图所示,我们需要请求的网址是:https://graph.qq.com/oauth2.0/authorize; 同时,我们还需要将response_type,client_id, redirect_uri,state这几个参数放在链接后面携带过去
后端生成登陆地址(生成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返回给前端,此时用户通过扫描二维码,进行授权登陆,用户登陆成功。
在用户授权确认登陆的时候,QQ会将返回回调页面 redirect_uri.html,当然,此时,QQ返回的参数中,还携带了code
和state
参数(上面传入的next参数的内容);
此时用户需要访问我们的回调页面 redirect_uri.html,当然请求的时候,除了回调页面,还会携带参数code和state:
访问链接比如: GET /oauth/qq/user/?code=xxx
;
此时,后端可以通过查询字符串的参数获取方式,获取到code数据
Step2:通过Authorization Code获取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]
Step3:通过Access Token 获取获取用户OpenID_OAuth2.0
上一步已经通过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