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的条件语句与运算符优先级详解
Oct 13 Python
python魔法方法-自定义序列详解
Jul 21 Python
Python实现Youku视频批量下载功能
Mar 14 Python
Python自动发邮件脚本
Mar 31 Python
Python tkinter事件高级用法实例
Jan 31 Python
让你Python到很爽的加速递归函数的装饰器
May 26 Python
用Python实现最速下降法求极值的方法
Jul 10 Python
python join方法使用详解
Jul 30 Python
python采集百度搜索结果带有特定URL的链接代码实例
Aug 30 Python
基于python爬取梨视频实现过程解析
Nov 09 Python
python 发送邮件的示例代码(Python2/3都可以直接使用)
Dec 03 Python
jupyter 添加不同内核的操作
Feb 06 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中通过ADO调用Access数据库的方法测试不通过
2006/12/31 PHP
PHP和XSS跨站攻击的防范
2007/04/17 PHP
CodeIgniter采用config控制的多语言实现根据浏览器语言自动转换功能
2014/07/18 PHP
php使用正则验证中文
2016/04/06 PHP
js CSS操作方法集合
2008/10/31 Javascript
Document 对象的常用方法
2009/07/31 Javascript
javascript addBookmark 加入收藏 多浏览器兼容
2009/08/15 Javascript
JQury slideToggle闪烁问题及解决办法
2011/07/05 Javascript
在百度知道团队中快速审批新成员的js脚本
2014/02/02 Javascript
基于javascript实现图片左右切换效果
2016/01/25 Javascript
Javascript农历与公历相互转换的简单实例
2016/10/09 Javascript
基于JavaScript实现全选、不选和反选效果
2017/02/15 Javascript
浅析JS中的 map, filter, some, every, forEach, for in, for of 用法总结
2017/03/29 Javascript
详解Vue.use自定义自己的全局组件
2017/06/14 Javascript
基于vue配置axios的方法步骤
2017/11/09 Javascript
vue 组件内获取actions的response方式
2019/11/08 Javascript
[00:32]2018DOTA2亚洲邀请赛EG出场
2018/04/03 DOTA
Python实现的一个简单LRU cache
2014/09/26 Python
Python的迭代器和生成器使用实例
2015/01/14 Python
解决python中用matplotlib画多幅图时出现图形部分重叠的问题
2019/07/07 Python
python实现基于朴素贝叶斯的垃圾分类算法
2019/07/09 Python
利用Python绘制Jazz网络图的例子
2019/11/21 Python
python GUI库图形界面开发之PyQt5日期时间控件QDateTimeEdit详细使用方法与实例
2020/02/27 Python
浅析Python 序列化与反序列化
2020/08/05 Python
CSS3实现时间轴效果
2016/07/11 HTML / CSS
Skyscanner台湾:全球知名的旅行比价引擎
2018/07/01 全球购物
高级运动鞋:GREATS
2019/07/19 全球购物
最新优秀教师个人先进事迹材料
2014/05/06 职场文书
节约用电标语
2014/06/17 职场文书
2014年库房工作总结
2014/11/26 职场文书
社区植树节活动总结
2015/02/06 职场文书
会议通知
2015/04/15 职场文书
暑期工社会实践报告
2015/07/13 职场文书
MySQL 可扩展设计的基本原则
2021/05/14 MySQL
python_tkinter事件类型详情
2022/03/20 Python
Python数据可视化之Seaborn的安装及使用
2022/04/19 Python