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基本数据类型详细介绍
Mar 11 Python
Python pass 语句使用示例
Mar 11 Python
python实现查询苹果手机维修进度
Mar 16 Python
python 网络爬虫初级实现代码
Feb 27 Python
python爬虫_实现校园网自动重连脚本的教程
Apr 22 Python
解决在pycharm中显示额外的 figure 窗口问题
Jan 15 Python
Pandas分组与排序的实现
Jul 23 Python
Python Pillow.Image 图像保存和参数选择方式
Jan 09 Python
python DES加密与解密及hex输出和bs64格式输出的实现代码
Apr 13 Python
Python虚拟环境的创建和包下载过程分析
Jun 19 Python
Python2.6版本pip安装步骤解析
Aug 17 Python
如何设置PyCharm中的Python代码模版(推荐)
Nov 20 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
西德产收音机
2021/03/01 无线电
php 模拟POST提交的2种方法详解
2013/06/17 PHP
PHP编写学校网站上新生注册登陆程序的实例分享
2016/03/21 PHP
php5.3/5.4/5.5/5.6/7常见新增特性汇总整理
2020/02/27 PHP
腾讯的ip接口 方便获取当前用户的ip地理位置
2010/11/25 Javascript
实用的JS正则表达式(手机号码/IP正则/邮编正则/电话等)
2013/01/11 Javascript
javascript ie6兼容position:fixed实现思路
2013/04/01 Javascript
jquery实现显示已选用户
2014/07/21 Javascript
javascript中实现兼容JAVA的hashCode算法代码分享
2020/08/11 Javascript
深入浅析javascript立即执行函数
2015/10/23 Javascript
解决JavaScript数字精度丢失问题的方法
2015/12/03 Javascript
Spring MVC中Ajax实现二级联动的简单实例
2016/07/06 Javascript
必备的JS调试技巧汇总
2016/07/20 Javascript
最实用的jQuery分页插件
2016/10/09 Javascript
electron demo项目npm install安装失败的解决方法
2018/02/06 Javascript
vue.js,ajax渲染页面的实例
2018/02/11 Javascript
JS合并两个数组的3种方法详解
2019/10/24 Javascript
[01:00:35]2018DOTA2亚洲邀请赛3月30日B组 EffcetVSMineski
2018/03/31 DOTA
Python中使用items()方法返回字典元素对的教程
2015/05/21 Python
Random 在 Python 中的使用方法
2018/08/09 Python
使用pygame编写Flappy bird小游戏
2020/03/14 Python
django表单中的按钮获取数据的实例分析
2020/07/31 Python
Python实现播放和录制声音的功能
2020/08/12 Python
Django REST Framework 分页(Pagination)详解
2020/11/30 Python
python的dict判断key是否存在的方法
2020/12/09 Python
台湾菁英交友:结识黄金单身的台湾人
2018/01/22 全球购物
手工制作的意大利皮革运动鞋:KOIO
2020/01/05 全球购物
澳大利亚领先的在线礼品网站:Gifts Australia
2020/08/15 全球购物
联强国际笔试题面试题
2013/07/10 面试题
机械设计专业应届生求职信
2013/11/21 职场文书
会计电算化专业求职信
2014/06/10 职场文书
分居协议书范本
2014/11/03 职场文书
小学教师年度个人总结
2015/02/05 职场文书
五一劳动节活动总结
2015/02/09 职场文书
用电申请报告范文
2015/05/18 职场文书
go xorm框架的使用
2021/05/22 Golang