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的Flask框架中验证注册用户的Email的方法
Sep 02 Python
python中string模块各属性以及函数的用法介绍
May 30 Python
python将文本分每两行一组并保存到文件
Mar 19 Python
对python中两种列表元素去重函数性能的比较方法
Jun 29 Python
使用python将请求的requests headers参数格式化方法
Jan 02 Python
Python实现去除列表中重复元素的方法总结【7种方法】
Feb 16 Python
jenkins配置python脚本定时任务过程图解
Oct 29 Python
pytorch模型存储的2种实现方法
Feb 14 Python
python输出第n个默尼森数的实现示例
Mar 08 Python
浅析Python面向对象编程
Jul 10 Python
Python 串口通信的实现
Sep 29 Python
python用字节处理文件实例讲解
Apr 13 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
Smarty的配置与高级缓存技术分享
2012/06/05 PHP
php判断两个浮点数是否相等的方法
2015/03/14 PHP
php微信公众平台开发(四)回复功能开发
2016/12/06 PHP
PHP对象相关知识总结
2017/04/09 PHP
PHP 进程池与轮询调度算法实现多任务的示例代码
2019/11/26 PHP
jQuery 动态酷效果实现总结
2009/12/27 Javascript
jQuery实现的类flash菜单效果代码
2010/05/17 Javascript
Jquery.addClass始终无效原因分析
2013/09/08 Javascript
Javascript 按位与运算符 (&amp;)使用介绍
2014/02/04 Javascript
jquery实现不同大小浏览器使用不同的css样式表的方法
2014/04/02 Javascript
node.js中的forEach()是同步还是异步呢
2015/01/29 Javascript
JS设置网页图片vspace和hspace属性的方法
2015/04/01 Javascript
JavaScript函数内部属性和函数方法实例详解
2016/03/17 Javascript
jQuery实现的简单对话框拖动功能示例
2018/06/05 jQuery
[06:10]6.81新信使新套装!给你一个炫酷的DOTA2
2014/05/06 DOTA
python处理PHP数组文本文件实例
2014/09/18 Python
在Python中用split()方法分割字符串的使用介绍
2015/05/20 Python
python+matplotlib绘制简单的海豚(顶点和节点的操作)
2018/01/02 Python
Python实现微信好友的数据分析
2019/12/16 Python
Python连接SQLite数据库并进行增册改查操作方法详解
2020/02/18 Python
阿迪达斯新加坡官方网站:adidas新加坡
2019/12/06 全球购物
电信专业应届生自荐信
2013/09/28 职场文书
店长岗位职责
2013/11/21 职场文书
庆中秋节主题活动方案
2014/02/03 职场文书
《青海高原一株柳》教学反思
2014/04/25 职场文书
2014物价局民主生活会对照检查材料思想汇报
2014/09/24 职场文书
乡镇遵守党的政治纪律情况对照检查材料
2014/09/26 职场文书
综治维稳工作汇报
2014/10/27 职场文书
毕业生班级鉴定评语
2015/01/04 职场文书
英文升职感谢信
2015/01/23 职场文书
个人培训总结
2015/03/05 职场文书
酒店总经理岗位职责
2015/04/01 职场文书
统招统分证明
2015/06/23 职场文书
土木工程生产实习心得体会
2016/01/22 职场文书
Python超详细分步解析随机漫步
2022/03/17 Python
Python如何加载模型并查看网络
2022/07/15 Python