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将html转成PDF的实现代码(包含中文)
Mar 04 Python
Python中的id()函数指的什么
Oct 17 Python
获取python文件扩展名和文件名方法
Feb 02 Python
Python实现对文件进行单词划分并去重排序操作示例
Jul 10 Python
CentOS 7下安装Python3.6 及遇到的问题小结
Nov 08 Python
一篇文章搞懂Python的类与对象名称空间
Dec 10 Python
Python理解递归的方法总结
Jan 28 Python
Python实现多线程/多进程的TCP服务器
Sep 03 Python
Tensorflow使用Anaconda、pycharm安装记录
Jul 29 Python
详解Python模块化编程与装饰器
Jan 16 Python
Python数据分析之pandas读取数据
Jun 02 Python
Python人工智能之混合高斯模型运动目标检测详解分析
Nov 07 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性能的21种方法介绍
2013/06/25 PHP
php中执行系统命令的方法
2015/03/21 PHP
PHP基于PDO实现的SQLite操作类【包含增删改查及事务等操作】
2017/06/21 PHP
js中将多个语句写成一个语句的两种方法小结
2007/12/08 Javascript
ExtJs GridPanel简单的增删改实现代码
2010/08/26 Javascript
基于jQuery的input输入框下拉提示层(自动邮箱后缀名)
2012/06/14 Javascript
jquery提交form表单时禁止重复提交的方法
2014/02/13 Javascript
jQuery实现html表格动态添加新行的方法
2015/05/28 Javascript
JS+CSS实现表格高亮的方法
2015/08/05 Javascript
基于jquery实现页面滚动时顶部导航显示隐藏
2020/04/20 Javascript
Javascript的表单与验证-非空验证
2016/03/18 Javascript
Bootstrap3使用typeahead插件实现自动补全功能
2016/07/07 Javascript
详解webpack和webpack-simple中如何引入css文件
2017/06/28 Javascript
JS如何实现在页面上快速定位(锚点跳转问题)
2017/08/14 Javascript
jQuery UI 实例讲解 - 日期选择器(Datepicker)
2017/09/18 jQuery
完美解决mui框架off-canvas侧滑超出部分隐藏无法滚动的问题
2018/01/25 Javascript
详解自定义ajax支持跨域组件封装
2018/02/08 Javascript
AngularJS修改model值时,显示内容不变的实例
2018/09/13 Javascript
vue-cli 3.x 修改dist路径的方法
2018/09/19 Javascript
Vue中util的工具函数实例详解
2019/07/08 Javascript
Vuex的API文档说明详解
2020/02/05 Javascript
解决vue-router路由拦截造成死循环问题
2020/08/05 Javascript
Python中线程编程之threading模块的使用详解
2015/06/23 Python
python实现给微信指定好友定时发送消息
2019/04/29 Python
Python3 文章标题关键字提取的例子
2019/08/26 Python
Django框架ORM数据库操作实例详解
2019/11/07 Python
Django 解决model 反向引用中的related_name问题
2020/05/19 Python
CSS3实现银灰色动画效果的导航菜单代码
2015/09/01 HTML / CSS
解决Firefox下不支持outerHTML问题代码分享
2014/06/04 HTML / CSS
瑞典网上购买现代和复古家具:Reforma
2019/10/21 全球购物
家长对孩子评语
2014/01/30 职场文书
酒店管理专业自荐信
2014/05/23 职场文书
英语专业求职信
2014/07/08 职场文书
员工离职证明范本
2015/06/12 职场文书
新年寄语2016
2015/08/17 职场文书
fastdfs+nginx集群搭建的实现
2021/03/31 Servers