Python的Flask框架应用程序实现使用QQ账号登录的方法


Posted in Python onJune 07, 2016

Flask-OAuthlib是OAuthlib的Flask扩展实现,
项目地址:
https://github.com/lepture/flask-oauthlib
主要特性:

  • 支持OAuth 1.0a, 1.0, 1.1, OAuth2客户端
  • 友好的API(和Flask-OAuth一样)
  • 与Flask直接整合
  • 等等……

Flask-OAuthlib提供了多个开放平台的示例代码,比如Google, Facebook, Twiter, Github, Dropbox, 豆瓣, 微博等,只是暂时没有QQ登录的示例代码。

QQ OAuth登录示例
下面是QQ登录的代码:

import os
import json
from flask import Flask, redirect, url_for, session, request, jsonify, Markup
from flask_oauthlib.client import OAuth

QQ_APP_ID = os.getenv('QQ_APP_ID', '101187283')
QQ_APP_KEY = os.getenv('QQ_APP_KEY', '993983549da49e384d03adfead8b2489')

app = Flask(__name__)
app.debug = True
app.secret_key = 'development'
oauth = OAuth(app)

qq = oauth.remote_app(
  'qq',
  consumer_key=QQ_APP_ID,
  consumer_secret=QQ_APP_KEY,
  base_url='https://graph.qq.com',
  request_token_url=None,
  request_token_params={'scope': 'get_user_info'},
  access_token_url='/oauth2.0/token',
  authorize_url='/oauth2.0/authorize',
)


def json_to_dict(x):
  '''OAuthResponse class can't not parse the JSON data with content-type
  text/html, so we need reload the JSON data manually'''
  if x.find('callback') > -1:
    pos_lb = x.find('{')
    pos_rb = x.find('}')
    x = x[pos_lb:pos_rb + 1]
  try:
    return json.loads(x, encoding='utf-8')
  except:
    return x


def update_qq_api_request_data(data={}):
  '''Update some required parameters for OAuth2.0 API calls'''
  defaults = {
    'openid': session.get('qq_openid'),
    'access_token': session.get('qq_token')[0],
    'oauth_consumer_key': QQ_APP_ID,
  }
  defaults.update(data)
  return defaults


@app.route('/')
def index():
  '''just for verify website owner here.'''
  return Markup('''<meta property="qc:admins" '''
         '''content="226526754150631611006375" />''')


@app.route('/user_info')
def get_user_info():
  if 'qq_token' in session:
    data = update_qq_api_request_data()
    resp = qq.get('/user/get_user_info', data=data)
    return jsonify(status=resp.status, data=resp.data)
  return redirect(url_for('login'))


@app.route('/login')
def login():
  return qq.authorize(callback=url_for('authorized', _external=True))


@app.route('/logout')
def logout():
  session.pop('qq_token', None)
  return redirect(url_for('get_user_info'))


@app.route('/login/authorized')
def authorized():
  resp = qq.authorized_response()
  if resp is None:
    return 'Access denied: reason=%s error=%s' % (
      request.args['error_reason'],
      request.args['error_description']
    )
  session['qq_token'] = (resp['access_token'], '')

  # Get openid via access_token, openid and access_token are needed for API calls
  resp = qq.get('/oauth2.0/me', {'access_token': session['qq_token'][0]})
  resp = json_to_dict(resp.data)
  if isinstance(resp, dict):
    session['qq_openid'] = resp.get('openid')

  return redirect(url_for('get_user_info'))


@qq.tokengetter
def get_qq_oauth_token():
  return session.get('qq_token')


if __name__ == '__main__':
  app.run()

主要流程:

  • 访问QQ互联网站 http://connect.qq.com/ 注册成为开发者,并申请应用,申请应用时需要验证网站所有权;
  • 应用申请好之后,把QQ_APP_ID和QQ_APP_KEY替换为你的应用的;
  • 访问/login,然后会跳转到QQ的授权验证网页;
  • QQ验证通过之后,会跳转回到/login/authorized,并获取access_token;
  • 得到access_token之后,通过access_token获取openid,access_token和openid是后期调用其它API的必要参数;
  • 跳转到/user_info,获取并显示登录用户的基本信息。

更多信息请参阅Flask-OAuthlib文档和QQ互联文档:

https://flask-oauthlib.readthedocs.org/
http://wiki.connect.qq.com/
关于SAE平台的特别说明
在SAE平台上,授权过程没有任何问题,当获取到access_token之后,调用API时,会在请求时(比如get, put)附加类似如下的请求头:

headers = {u'Authorization': u'Bearer 83F40E96FB6882686F4DF1E17105D04E'}

这个请求头会引发HTTPError: HTTP Error 400: Bad request,造成请求失败。解决的办法是把键名转换成str类型,Hack代码如下:

def convert_keys_to_string(dictionary):
  """Recursively converts dictionary keys to strings."""
  if not isinstance(dictionary, dict):
    return dictionary
  return dict((str(k), convert_keys_to_string(v))
    for k, v in dictionary.items())

def change_qq_header(uri, headers, body):
  headers = convert_keys_to_string(headers)
  return uri, headers, body

qq.pre_request = change_qq_header

当项目部署在SAE平台时,将这段代码放在if __name__ == '__main__'语句之前即可。

小结
OAuth2登录验证还是比较容易的,绝大多数的平台都支持标准的协议,使用通用的库可以简化开发流程。另外,QQ登录的代码已经提交到Flask-OAuthlib代码库了。

Python 相关文章推荐
python进阶教程之函数对象(函数也是对象)
Aug 30 Python
Windows下实现Python2和Python3两个版共存的方法
Jun 12 Python
python3中set(集合)的语法总结分享
Mar 24 Python
python绘制双柱形图代码实例
Dec 14 Python
详解Python异常处理中的Finally else的功能
Dec 29 Python
Python设计模式之命令模式简单示例
Jan 10 Python
python 找出list中最大或者最小几个数的索引方法
Oct 30 Python
Python八皇后问题解答过程详解
Jul 29 Python
python防止随意修改类属性的实现方法
Aug 21 Python
Pytorch 中retain_graph的用法详解
Jan 07 Python
tensorflow 实现数据类型转换
Feb 17 Python
matplotlib绘制鼠标的十字光标的实现(内置方式)
Jan 06 Python
在CentOS上配置Nginx+Gunicorn+Python+Flask环境的教程
Jun 07 #Python
Windows上使用virtualenv搭建Python+Flask开发环境
Jun 07 #Python
在Python的Flask中使用WTForms表单框架的基础教程
Jun 07 #Python
详解Python的Flask框架中生成SECRET_KEY密钥的方法
Jun 07 #Python
Python的Flask框架中配置多个子域名的方法讲解
Jun 07 #Python
python3批量删除豆瓣分组下的好友的实现代码
Jun 07 #Python
python实现多线程的方式及多条命令并发执行
Jun 07 #Python
You might like
php实现utf-8转unicode函数分享
2015/01/06 PHP
PHP和Shell实现检查SAMBA与NFS Server是否存在
2015/01/07 PHP
SCP远程VPS快速搬家和WDCP升级php5.3安装memcached和eaccelerator教程
2017/07/27 PHP
jquery中html、val与text三者属性取值的联系与区别介绍
2013/12/29 Javascript
详解JavaScript语法对{}处理的坑爹之处
2014/06/05 Javascript
jquery显示隐藏input对象
2014/07/21 Javascript
3种Jquery限制文本框只能输入数字字母的方法
2014/12/03 Javascript
javascript计时器详解
2015/02/28 Javascript
jquery中map函数遍历数组用法实例
2015/05/18 Javascript
Vue结合原生js实现自定义组件自动生成示例
2017/01/21 Javascript
深入浅析Vue不同场景下组件间的数据交流
2017/08/15 Javascript
浅谈在node.js进入文件目录的问题
2018/05/13 Javascript
Vue组件间通信 Vuex的用法解析
2019/08/05 Javascript
Vue实现购物车实例代码两则
2020/05/30 Javascript
原生js+canvas实现验证码
2020/11/29 Javascript
[02:10]三分钟回顾完美世界城市挑战赛
2019/01/24 DOTA
python相似模块用例
2016/03/04 Python
python3对接mysql数据库实例详解
2019/04/30 Python
python 函数中的内置函数及用法详解
2019/07/02 Python
在windows下使用python进行串口通讯的方法
2019/07/02 Python
Pandas DataFrame中的tuple元素遍历的实现
2019/10/23 Python
利用python实现PSO算法优化二元函数
2019/11/13 Python
使用Python实现画一个中国地图
2019/11/23 Python
在tensorflow中实现屏蔽输出的log信息
2020/02/04 Python
python实现简单颜色识别程序
2020/02/19 Python
Python基于yield遍历多个可迭代对象
2020/03/12 Python
Python常用类型转换实现代码实例
2020/07/28 Python
简单了解Python字典copy与赋值的区别
2020/09/16 Python
Html5 语法与规则简要概述
2014/07/29 HTML / CSS
AmazeUI 评论列表的实现示例
2020/08/13 HTML / CSS
Larsson & Jennings官网:现代瑞士钟表匠
2018/03/20 全球购物
简述数组与指针的区别
2014/01/02 面试题
教师研修随笔感言
2014/01/23 职场文书
xxx同志考察材料
2014/02/07 职场文书
捐资助学感谢信
2015/01/21 职场文书
九九重阳节致辞
2015/07/31 职场文书