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连接池实现示例程序
Nov 26 Python
Python程序设计入门(1)基本语法简介
Jun 13 Python
python 获取文件下所有文件或目录os.walk()的实例
Apr 23 Python
python中ASCII码和字符的转换方法
Jul 09 Python
Python删除n行后的其他行方法
Jan 28 Python
简单了解python中的与或非运算
Sep 18 Python
Python3和PyCharm安装与环境配置【图文教程】
Feb 14 Python
python批量修改xml属性的实现方式
Mar 05 Python
基于python生成英文版词云图代码实例
May 16 Python
浅析Python 简单工厂模式和工厂方法模式的优缺点
Jul 13 Python
Python return语句如何实现结果返回调用
Oct 15 Python
python des,aes,rsa加解密的实现
Jan 16 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/02 无线电
十天学会php之第十天
2006/10/09 PHP
让PHP更快的提供文件下载的代码
2012/06/13 PHP
Ubuntu12下编译安装PHP5.3开发环境
2015/03/27 PHP
1亿条数据如何分表100张到Mysql数据库中(PHP)
2015/07/29 PHP
PHP常见错误提示含义解释(实用!值得收藏)
2016/04/25 PHP
详解JS函数重载
2014/12/04 Javascript
javascript 构造函数方式定义对象
2015/01/02 Javascript
js封装可使用的构造函数继承用法分析
2015/01/28 Javascript
jQuery实现Tab菜单滚动切换的方法
2015/09/21 Javascript
js实现的简练高效拖拽功能示例
2016/12/21 Javascript
通俗易懂地解释JS中的闭包
2017/10/23 Javascript
Nuxt配合Node在实际生产中的应用详解
2018/08/07 Javascript
微信小程序数据分析之自定义分析的实现
2018/08/17 Javascript
微信小程序中this.data与this.setData的区别详解
2018/09/17 Javascript
解决vue-cli脚手架打包后vendor文件过大的问题
2018/09/27 Javascript
[26:40]DOTA2上海特级锦标赛A组资格赛#1 Secret VS MVP.Phx第一局
2016/02/25 DOTA
Python的Urllib库的基本使用教程
2015/04/30 Python
详解Python中的Descriptor描述符类
2016/06/14 Python
python+matplotlib演示电偶极子实例代码
2018/01/12 Python
python实现屏保计时器的示例代码
2018/08/08 Python
解决Jupyter notebook中.py与.ipynb文件的import问题
2020/04/21 Python
如何使用localstorage代替cookie实现跨域共享数据问题
2018/04/18 HTML / CSS
HTML5中原生的右键菜单创建方法
2016/06/28 HTML / CSS
THE OUTNET英国官网:国际设计师品牌折扣网站
2016/08/14 全球购物
蛋白质世界:Protein World
2017/11/23 全球购物
The Athlete’s Foot新西兰:新西兰最大的运动鞋零售商
2019/12/23 全球购物
威盛公司软件C++工程师笔试题面试题
2012/07/16 面试题
Python使用openpyxl复制整张sheet
2021/03/24 Python
教师学习培训邀请函
2014/02/04 职场文书
挂靠协议书范本
2014/04/22 职场文书
2015年元旦演讲稿
2014/09/12 职场文书
武夷山导游词
2015/02/03 职场文书
未婚证明范本
2015/06/15 职场文书
解决Jupyter-notebook不弹出默认浏览器的问题
2021/03/30 Python
Python中的min及返回最小值索引的操作
2021/05/10 Python