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 相关文章推荐
Pyramid Mako模板引入helper对象的步骤方法
Nov 27 Python
Python从MP3文件获取id3的方法
Jun 15 Python
使用Python来开发Markdown脚本扩展的实例分享
Mar 04 Python
Python备份目录及目录下的全部内容的实现方法
Jun 12 Python
实例讲解Python编程中@property装饰器的用法
Jun 20 Python
Python实现螺旋矩阵的填充算法示例
Dec 28 Python
100行Python代码实现自动抢火车票(附源码)
Jan 11 Python
Flask模拟实现CSRF攻击的方法
Jul 24 Python
PyQt QCombobox设置行高的方法
Jun 20 Python
python GUI库图形界面开发之PyQt5 UI主线程与耗时线程分离详细方法实例
Feb 26 Python
使用Django xadmin 实现修改时间选择器为不可输入状态
Mar 30 Python
通过实例解析python创建进程常用方法
Jun 19 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
WindowsXP中快速配置Apache+PHP5+Mysql
2008/06/05 PHP
ajax+php实现无刷新验证手机号的实例
2017/12/22 PHP
PHP chop()函数讲解
2019/02/11 PHP
javascript字典探测用户名工具
2006/10/05 Javascript
Javascript(AJAX)解析XML的代码(兼容FIREFOX/IE)
2010/07/11 Javascript
jquery.artwl.thickbox.js  一个非常简单好用的jQuery弹出层插件
2012/03/01 Javascript
JavaScript 用cloneNode方法克隆节点的代码
2012/10/15 Javascript
JavaScript中OnLoad几种使用方法
2012/12/15 Javascript
JS字符串拼接在ie中都报错的解决方法
2014/03/27 Javascript
jQuery垂直多级导航菜单代码分享
2015/08/18 Javascript
简单实现的JQuery文本框水印插件
2016/06/14 Javascript
完全深入学习Bootstrap表单
2016/11/28 Javascript
jQuery实现联动下拉列表查询框
2017/01/04 Javascript
jQuery上传多张图片带进度条样式(DEMO)
2017/03/02 Javascript
JavaScript实现打地鼠小游戏
2020/04/23 Javascript
Vue-router路由判断页面未登录跳转到登录页面的实例
2017/10/26 Javascript
vue  自定义组件实现通讯录功能
2018/09/30 Javascript
js实现动态增加文件域表单功能
2018/10/22 Javascript
在vue中根据光标的显示与消失实现下拉列表
2019/09/29 Javascript
linux系统使用python获取cpu信息脚本分享
2014/01/15 Python
python使用正则表达式提取网页URL的方法
2015/05/26 Python
Python入门学习之字符串与比较运算符
2015/10/12 Python
简单了解python gevent 协程使用及作用
2019/07/22 Python
基于Django框架的权限组件rbac实例讲解
2019/08/31 Python
Python下利用BeautifulSoup解析HTML的实现
2020/01/17 Python
pycharm 设置项目的根目录教程
2020/02/12 Python
Pandas将列表(List)转换为数据框(Dataframe)
2020/04/24 Python
Python使用for生成列表实现过程解析
2020/09/22 Python
html5 音乐播放器 audio 标签使用概述
2013/07/15 HTML / CSS
日本高端护肤品牌:Tatcha
2016/08/29 全球购物
Omio美国:全欧洲低价大巴、火车和航班搜索和比价
2017/11/08 全球购物
波兰在线儿童和婴儿用品零售商:pinkorblue
2019/06/29 全球购物
北京RT科技有限公司.net工程师面试题
2013/02/15 面试题
《口技》教学反思
2014/02/21 职场文书
《飞向蓝天的恐龙》教学反思
2014/04/09 职场文书
2015年食品安全工作总结
2015/05/15 职场文书