FLASK中的鉴权的插件Flask-HTTPAuth

2023-09-17 22:21:02

在 Web 应用中,我们经常需要保护我们的 api,以避免非法访问。比如,只允许登录成功的用户发表评论等。Flask-HTTPAuth 扩展可以很好地对 HTTP 的请求进行认证,不依赖于 Cookie 和 Session。本文主要介绍两种认证的方式:基于密码和基于令牌 (token)。

1、安装

$ pip install Flask-HTTPAuth

2、基于密码的认证

为了简化代码,这里我们就不引入数据库了。

  • 首先,创建扩展对象实例
from flask import Flask
from flask_httpauth import HTTPBasicAuth
app = Flask(__name__)
auth = HTTPBasicAuth()

这里有一点需要注意的是,我们创建了一个 auth 对象,但没有传入 app 对象,这跟其他扩展初始化实例有一点区别。

  • 接着,写一个验证用户密码的回调函数
from werkzeug.security import generate_password_hash, check_password_hash
# 模拟数据库
books = ['The Name of the Rose', 'The Historian', 'Rebecca']
users = [
    {'username': 'ethan', 'password': generate_password_hash('6666')},
    {'username': 'peter', 'password': generate_password_hash('4567')}
]
# 回调函数
@auth.verify_password
def verify_password(username, password):
    user = filter(lambda user: user['username'] == username, users)
    if user and check_password_hash(user[0]['password'], password):
        g.user = username
        return True
    return False

上面,为了对密码进行加密以及认证,我们使用 werkzeug.security 包提供的 generate_password_hash 和 check_password_hash 方法:generate_password_hash 会对给定的字符串,生成其加盐的哈希值;check_password_hash 验证传入的明文字符串与哈希值是否一致。

  • 然后,我们在需要认证的视图函数上,加上 @auth.login_required 装饰器,比如
@app.route('/', methods=['POST'])
@auth.login_required
def add_book():
    _form = request.form
    title = _form["title"]
    if not title:
        return '<h1>invalid request</h1>'
    books.append(title)
    flash("add book successfully!")
    return redirect(url_for('index'))

上面完整的代码如下:

$ cat app.py
# -*- coding: utf-8 -*-
from flask import Flask, url_for, render_template, request, flash, \
    redirect, make_response, jsonify, g
from werkzeug.security import generate_password_hash, check_password_hash
from flask_httpauth import HTTPBasicAuth
app = Flask(__name__)
app.config['SECRET_KEY'] = 'secret key'
auth = HTTPBasicAuth()
# 模拟数据库
books = ['The Name of the Rose', 'The Historian', 'Rebecca']
users = [
    {'username': 'ethan', 'password': generate_password_hash('6666')},
    {'username': 'peter', 'password': generate_password_hash('4567')}
]
# 回调函数
@auth.verify_password
def verify_password(username, password):
    user = filter(lambda user: user['username'] == username, users)
    if user and check_password_hash(user[0]['password'], password):
        g.user = username
        return True
    return False
# 不需认证,可直接访问
@app.route('/', methods=['GET'])
def index():
    return render_template(
        'book.html',
        books=books
    )
# 需要认证
@app.route('/', methods=['POST'])
@auth.login_required
def add_book():
    _form = request.form
    title = _form["title"]
    if not title:
        return '<h1>invalid request</h1>'
    books.append(title)
    flash("add book successfully!")
    return redirect(url_for('index'))
@auth.error_handler
def unauthorized():
    return make_response(jsonify({'error': 'Unauthorized access'}), 401)
if __name__ == '__main__':
    app.run(host='127.0.0.1', port=5206, debug=True)
$ cat templates/layout.html
<!doctype html>
<title>Hello Sample</title>
<div class="page">
    {% block body %} {% endblock %}
</div>
{% for message in get_flashed_messages() %}
    {{ message }}
{% endfor %}
$ cat templates/book.html
{% extends "layout.html" %}
{% block body %}
{% if books %}
    {% for book in books %}
        <ul>
            <li> {{ book }} </li>
        </ul>
    {% endfor %}
{% else %}
    <p> The book doesn't exists! </p>
{% endif %}
<form method="post" action="{{ url_for('add_book') }}">
    <input id="title" name="title" placeholder="add book" type="text">
    <button type="submit">Submit</button>
</form>
{% endblock %}

3、基于 token 的认证

很多时候,我们并不直接通过密码做认证,比如当我们把 api 开放给第三方的时候,我们不可能给它们提供密码,而是对它们进行授权,还可能会有时间限制,比如半年或一年等。这时候,我们往往通过一个令牌,也就是 token 来做认证,Flask-HTTPAuth 提供了 HTTPTokenAuth 对象来做这件事。
例如:

from flask import Flask, g
from flask_httpauth import HTTPTokenAuth
app = Flask(__name__)
auth = HTTPTokenAuth(scheme='Token')
tokens = {
    "secret-token-1": "john",
    "secret-token-2": "susan"
}
# 回调函数,验证 token 是否合法
@auth.verify_token
def verify_token(token):
    if token in tokens:
        g.current_user = tokens[token]
        return True
    return False
# 需要认证
@app.route('/')
@auth.login_required
def index():
    return "Hello, %s!" % g.current_user
if __name__ == '__main__':
    app.run()

上面,我们在初始化 HTTPTokenAuth 对象时,传入了 scheme=‘Token’。这个 scheme,是我们在发送请求时,在 HTTP 头 Authorization 中要用的 scheme 字段。用 curl 测试如下:

$ curl -X GET -H "Authorization: Token secret-token-1" http://localhost:5000/

结果:

Hello, john!

4、使用 itsdangerous 库来管理令牌

上面的令牌还是比较薄弱的,在实际使用中,我们需要使用加密的签名(Signature)作为令牌,它能够根据用户信息生成相关的签名,并且很难被篡改。itsdangerous 提供了上述功能,在使用之前请使用 pip 安装: $ pip install itsdangerous。

改进后的代码如下:

# -*- coding: utf-8 -*-
from flask import Flask, g
from flask_httpauth import HTTPTokenAuth
from itsdangerous import TimedJSONWebSignatureSerializer as Serializer
app = Flask(__name__)
app.config['SECRET_KEY'] = 'secret key here'
auth = HTTPTokenAuth(scheme='Token')
# 实例化一个签名序列化对象 serializer,有效期 10 分钟
serializer = Serializer(app.config['SECRET_KEY'], expires_in=600)
users = ['john', 'susan']
# 生成 token
for user in users:
    token = serializer.dumps({'username': user})
    print('Token for {}: {}\n'.format(user, token))
# 回调函数,对 token 进行验证
@auth.verify_token
def verify_token(token):
    g.user = None
    try:
        data = serializer.loads(token)
    except:
        return False
    if 'username' in data:
        g.user = data['username']
        return True
    return False
# 对视图进行认证
@app.route('/')
@auth.login_required
def index():
    return "Hello, %s!" % g.user
if __name__ == '__main__':
    app.run()

将上面代码保存为 app.py,在终端运行,可看到类似如下的输出:

$ python app.py
Token for John: eyJhbGciOiJIUzI1NiIsImV4cCI6MTQ3NjY5NzE0NCwiaWF0IjoxNDc2Njk1MzQ0fQ.eyJ1c2VybmFtZSI6IkpvaG4ifQ.vQu0z0Pos2Tgt5jBYMY5IYWUkTK9k3wE_RqvYHDqtyM
Token for Susan: eyJhbGciOiJIUzI1NiIsImV4cCI6MTQ3NjY5NzE0NCwiaWF0IjoxNDc2Njk1MzQ0fQ.eyJ1c2VybmFtZSI6IlN1c2FuIn0.rk8JaTRwag0qiF9_KuRodhw6wx2ZWkOEhFln9hzOLP0
 * Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)

使用 curl 测试如下:

$ curl -X GET -H "Authorization: Token eyJhbGciOiJIUzI1NiIsImV4cCI6MTQ3NjY5NzE0NCwiaWF0IjoxNDc2Njk1MzQ0fQ.eyJ1c2VybmFtZSI6IkpvaG4ifQ.vQu0z0Pos2Tgt5jBYMY5IYWUkTK9k3wE_RqvYHDqtyM" http://localhost:5000/
# 结果
$ Hello, john!
更多推荐

Python终端优化:提高工作效率的关键步骤

💂个人网站:【工具大全】【游戏大全】【神级源码资源网】🤟前端学习课程:👉【28个案例趣学前端】【400个JS面试题】💅寻找学习交流、摸鱼划水的小伙伴,请点击【摸鱼学习交流群】导言:Python是一种强大的编程语言,广泛用于数据分析、Web开发、自动化脚本等各种领域。对于许多开发人员和数据科学家来说,Python

MyBatis 反射模块

文章目录前言反射模块实现ReflectorReflectorFactoryInvokerMetaClassMetaObject反射模块应用SqlSessionFactory执行SQL前言MyBatis在进行参数处理、结果集映射等操作时会使用到大量的反射操作,Java中的反射功能虽然强大,但是代码编写起来比较复杂且容易出

pywinauto:Windows桌面应用自动化测试(三)

前言上一篇文章地址:pywinauto:Windows桌面应用自动化测试(二)_LionKing的博客-CSDN博客下一篇文章地址:暂无一、win应用的后端技术1、官方文档安装了pywinauto后,如何开始使用呢?首先必须确定哪种辅助技术(pywinauto的后端)可以用于你的应用程序,注意粗体部分。在Windows

【LittleXi】第四章 Process Intro exercise

目录【LittleXi】第四章ProcessIntroexercise第四章实验准备问题【LittleXi】第四章ProcessIntroexercise第四章实验准备下载实验所需代码wgethttp://pages.cs.wisc.edu/~remzi/OSTEP/Homework/HW-CPU-Intro.tgz解

ArcGIS10.1软件安装教程

ArcGIS10.1中英文(32/64位)下载地址:链接:https://pan.baidu.com/s/1Ksm112WaKMMk6La9ircCng密码:t70f安装步骤:1、我们对安装包进行解压,直接鼠标右击解压即可。2、打开我们解压的文件夹,点击Desktop3、鼠标右击setup.exe图标,选择以管理员的身

电压放大电路的作用有哪些(电压放大器)

电压放大电路是电子电路中常见且重要的组件,其主要作用是将输入信号的电压放大到所需的输出电压级别,并保持输入信号的形状和准确度。电压放大电路广泛应用于各种电子设备和系统中,具有以下几个重要的作用:信号放大:电压放大电路可以将微弱的输入信号放大到较大的输出信号。在许多应用中,输入信号的电压太小无法直接应用,例如音频信号的放

Talk | KDD‘23 Best Paper 港中文孙相国:All in One - 提示学习在图神经网络中的探索

本期为TechBeat人工智能社区第532期线上Talk!北京时间9月20日(周三)20:00,香港中文大学博后研究员—孙相国的Talk已准时在TechBeat人工智能社区开播!他与大家分享的主题是:“提示学习在图神经网络中的探索”,他分享了提示学习基本概念,并介绍了他的团队提出的图模型多任务提示方法。Talk·信息▼

在Windows环境下,使用VS2013配置FFmpeg+OpenCV开发环境

在Windows环境下,使用VS2013配置FFmpeg+OpenCV开发环境的步骤如下:1.下载FFmpeg和OpenCV库,可以到官网或者其他网站下载最新稳定版的库。2.安装FFmpeg库,将FFmpeg安装目录添加到系统环境变量中。例如,将FFmpeg安装到C:/ffmpeg,则需要将C:/ffmpeg/bin路

图扑可视化图表组件之股票数据分析应用

股市是市场经济的必然产物,在一个国家的金融领域之中有着举足轻重的地位。在过去,人们对于市场走势的把握主要依赖于经验和直觉,往往容易受到主观因素的影响,导致决策上出现偏差。如今,通过数据可视化呈现,便可将历年数据和市场情报进行深度挖掘、分析,从中找到规律和趋势,帮助用户做出更准确的判断。回顾2022年A股市场的表现可谓是

JS操作字符串面试题系列(2)-每天做5题

目录前排提示JS操作字符串方法学习系列(1)-每天学习10个方法JS操作字符串方法学习系列(2)-每天学习10个方法JS操作字符串方法学习系列(3)-每天学习10个方法JS操作字符串方法学习系列(4)-每天学习10个方法JS操作字符串方法学习系列(5)-每天学习10个方法题目6.如何将字符串转换为大写?7.如何从字符串

【深度学习】 Python 和 NumPy 系列教程(十八):Matplotlib详解:2、3d绘图类型(4)3D曲面图(3D Surface Plot)

目录一、前言二、实验环境三、Matplotlib详解1、2d绘图类型2、3d绘图类型0.设置中文字体1.3D线框图(3DLinePlot)2.3D散点图(3DScatterPlot)3.3D条形图(3DBarPlot)4.3D曲面图(3DSurfacePlot)一、前言Python是一种高级编程语言,由Guidovan

热文推荐