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中字符串的修改及传参详解
Nov 30 Python
Python实现简单的多任务mysql转xml的方法
Feb 08 Python
Python3网络爬虫之使用User Agent和代理IP隐藏身份
Nov 23 Python
python生成ppt的方法
Jun 07 Python
python版飞机大战代码分享
Nov 20 Python
Python通过TensorFlow卷积神经网络实现猫狗识别
Mar 14 Python
记录Python脚本的运行日志的方法
Jun 05 Python
python中sort和sorted排序的实例方法
Aug 26 Python
python解析yaml文件过程详解
Aug 30 Python
python函数装饰器之带参数的函数和带参数的装饰器用法示例
Nov 06 Python
如何用 Python 制作 GitHub 消息助手
Feb 20 Python
Python简易开发之制作计算器
Apr 28 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读取二进制流(C语言结构体struct数据文件)的深入解析
2013/06/13 PHP
ThinkPHP项目分组配置方法分析
2016/03/23 PHP
PHP 断点续传实例详解
2017/11/11 PHP
php微信公众号开发之微信企业付款给个人
2018/10/04 PHP
CutePsWheel javascript libary 控制输入文本框为可使用滚轮控制的js库
2010/02/07 Javascript
jQuery-Easyui 1.2 实现多层菜单效果的代码
2012/01/13 Javascript
Function.prototype.bind用法示例
2013/09/16 Javascript
用Jquery实现滚动新闻
2014/02/12 Javascript
JavaScript之数组(Array)详解
2015/04/01 Javascript
jQuery实现的产品自动360度旋转展示特效源码分享
2015/08/21 Javascript
分享12个实用的jQuery代码片段
2016/03/09 Javascript
基于Bootstrap重置输入框内容按钮插件
2016/05/12 Javascript
JavaScript第一篇之实现按钮全选、功能
2016/08/21 Javascript
JavaScript实战(原生range和自定义特效)简单实例
2016/08/21 Javascript
详解Node.js:events事件模块
2016/11/24 Javascript
Angular2安装angular-cli
2017/05/21 Javascript
BootStrap Validator 根据条件在JS中添加或移除校验操作
2017/10/12 Javascript
微信小程序实现提交input信息到后台的方法示例
2019/01/19 Javascript
vue 中 beforeRouteEnter 死循环的问题
2019/04/23 Javascript
Node.js实现简单的爬取的示例代码
2019/06/25 Javascript
详解JavaScript 中的批处理和缓存
2020/11/19 Javascript
python翻译软件实现代码(使用google api完成)
2013/11/26 Python
Python如何实现守护进程的方法示例
2017/02/08 Python
Python3.6简单操作Mysql数据库
2017/09/12 Python
tensorflow创建变量以及根据名称查找变量
2018/03/10 Python
Python学习小技巧总结
2018/06/10 Python
python对列进行平移变换的方法(shift)
2019/01/10 Python
python Pandas库基础分析之时间序列的处理详解
2019/07/13 Python
djano一对一、多对多、分页实例代码
2019/08/16 Python
python shutil文件操作工具使用实例分析
2019/12/25 Python
selenium+headless chrome爬虫的实现示例
2021/01/08 Python
Html5页面内使用JSON动画的实现
2019/01/29 HTML / CSS
汇科协同Java笔试题
2012/03/31 面试题
卫校毕业生自我鉴定
2014/09/28 职场文书
保管员岗位职责
2015/02/14 职场文书
Python中异常处理用法
2021/11/27 Python