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利用matplotlib库绘制饼图的方法示例
Dec 18 Python
Python中查看文件名和文件路径
Mar 31 Python
python基本语法练习实例
Sep 19 Python
学习python中matplotlib绘图设置坐标轴刻度、文本
Feb 07 Python
python 通过 socket 发送文件的实例代码
Aug 14 Python
python将excel转换为csv的代码方法总结
Jul 03 Python
python KNN算法实现鸢尾花数据集分类
Oct 24 Python
python基于gevent实现并发下载器代码实例
Nov 01 Python
python3 Scrapy爬虫框架ip代理配置的方法
Jan 17 Python
python 解决selenium 中的 .clear()方法失效问题
Sep 01 Python
8g内存用python读取10文件_面试题-python 如何读取一个大于 10G 的txt文件?
May 28 Python
详细介绍python类及类的用法
May 31 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 CURL模拟GET及POST函数代码
2010/04/25 PHP
php对数组排序代码分享
2014/02/24 PHP
php返回json数据函数实例
2014/10/09 PHP
php中call_user_func函数使用注意事项
2014/11/21 PHP
php将图片保存为不同尺寸图片的图片类实例
2015/03/30 PHP
PHP程序员学习使用Swoole的理由
2018/06/24 PHP
iframe异步加载实现点击左边菜单加载右边内容实例讲解
2013/03/04 Javascript
浅谈javascript中自定义模版
2015/01/29 Javascript
jquery append 动态添加的元素事件on 不起作用的解决方案
2015/07/30 Javascript
JS实现从顶部下拉显示的带动画QQ客服特效代码
2015/10/24 Javascript
JS模拟实现方法重载示例
2016/08/03 Javascript
Bootstrap模态框使用详解
2017/02/15 Javascript
使用html+js+css 实现页面轮播图效果(实例讲解)
2017/09/21 Javascript
vue2手机APP项目添加开屏广告或者闪屏广告
2017/11/28 Javascript
vue登录路由验证的实现
2017/12/13 Javascript
详解Angular5/Angular6项目如何添加热更新(HMR)功能
2018/10/10 Javascript
React+TypeScript+webpack4多入口配置详解
2019/08/08 Javascript
vue element 中的table动态渲染实现(动态表头)
2019/11/21 Javascript
js实现视图和数据双向绑定的方法分析
2020/02/05 Javascript
原生js 实现表单验证功能
2021/02/08 Javascript
python和shell变量互相传递的几种方法
2013/11/20 Python
在Python中操作列表之List.append()方法的使用
2015/05/20 Python
python: line=f.readlines()消除line中\n的方法
2018/03/19 Python
使用Python批量修改文件名的代码实例
2019/01/24 Python
python实现局域网内实时通信代码
2019/12/22 Python
python多线程使用方法实例详解
2019/12/30 Python
Python转换itertools.chain对象为数组的方法
2020/02/07 Python
Django bulk_create()、update()与数据库事务的效率对比分析
2020/05/15 Python
Django用户登录与注册系统的实现示例
2020/06/03 Python
简单了解Python变量作用域正确使用方法
2020/06/12 Python
vue.js刷新当前页面的实例讲解
2020/12/29 Python
巴西购物网站:Onofre Agora
2020/06/08 全球购物
写自荐信要注意什么
2013/12/26 职场文书
2014组织生活会方案
2014/05/19 职场文书
2014年小学教导处工作总结
2014/12/19 职场文书
党支部审查意见
2015/06/02 职场文书