flask 扩展 (二)

579 阅读5分钟

Flask—Mail

简介

在开发过程中,很多应用程序都需要通过邮件提醒用户, Flask 的扩展包 Flask-Mail通过包装了 Python 内置的 smtplib 包,可以用在 Flask 程序中发送邮件。

Flask-Mail 连接到简单邮件协议(Simple Mail Transfer Protocol, SMTP)服务器,并把邮件交给服务器发送。

安装

pip install flask-mail

在模拟邮件服务器中进行测试

开启一个模拟的测试邮件服务器:

python -m smtpd -n -c DebuggingServer localhost:8025

该模拟邮件服务器所需的环境变量:

~ export MAIL_SERVER=localhost
~ export MAIL_PORT=8025

在程序中我们会将其加入 app 的配置中。

import os

from flask import Flask
from flask_mail import Mail, Message

app = Flask(__name__)


class Config(object):
    SECRET_KEY = os.environ.get('SECRET_KEY') or 'you-will-never-guess'
    MAIL_SERVER = os.environ.get('MAIL_SERVER', "localhost")
    MAIL_PORT = int(os.environ.get('MAIL_PORT') or 8025)
    ADMINS = ['2564493603@qq.com']


app.config.from_object(Config)

mail = Mail(app)


@app.route('/')
def index():
    # sender 发送方
    # recipients 接收方列表
    msg = Message("This is a test ", sender='2564493603@qq.com',
                  recipients=['ruiyang0715@gmail.com'])
    # 邮件内容
    msg.body = "Flask test mail"
    # 发送邮件
    mail.send(msg)
    print("Mail sent")
    return "Sent Succeed"


if __name__ == "__main__":
    app.run()

浏览器访问: http://127.0.0.1:5000/

在开启了 邮件服务器 的终端即可显示我们发送的邮件相关信息:

配置 163 邮件服务器

如果你希望真实地发送电子邮件,则需要使用真实的电子邮件服务器。 那么你只需要为它设置MAIL_SERVER、MAIL_PORT、MAIL_USE_TLS、MAIL_USERNAME和MAIL_PASSWORD环境变量。 如果你想要快速解决方案,可以使用 163邮箱 帐户发送电子邮件,并使用以下设置:

export MAIL_SERVER=smtp.163.com
export MAIL_PORT=465
export MAIL_USERNAME=15626046299@163.com
export MAIL_PASSWORD=your-password

示例:

补充说明

许多Flask插件需要应用上下文才能工作,因为这使得他们可以在不传递参数的情况下找到Flask应用实例。这些插件需要知道应用实例的原因是因为它们的配置存储在app.config对象中,这正是Flask-Mail的情况。mail.send()方法需要访问电子邮件服务器的配置值,而这必须通过访问应用属性的方式来实现。 使用with app.app_context()调用创建的应用上下文使得应用实例可以通过来自Flask的current_app变量来进行访问。

python-dotenv

在 flask 项目中,我们会通过设置FLASK_APP环境变量告诉 Flask 如何导入它:

export FLASK_APP=microblog.py

如果使用Microsoft Windows操作系统,在上面的命令中使用set替换export。

在终端会话中直接设置的环境变量不会永久生效,因此你不得不在每次新开终端时设定 FLASK_APP 环境变量,从 1.0 版本开始,Flask 允许你设置只会在运行 flask 命令时自动注册生效的环境变量,要实现这点,你需要安装 python-dotenv:

pip install python-dotenv

此时,在项目的根目录下新建一个名为 .flaskenv 的文件,其内容是:

FLASK_APP=microblog.py

通过此项设置,FLASK_APP就可以自动加载了。

pyjwt

什么是 JWT

JSON Web Token(JWT)是一个非常轻巧的规范。这个规范允许我们使用JWT在两个组织之间传递安全可靠的信息。

官方定义:JSON Web Token (JWT) is a compact URL-safe means of representing claims to be transferred between two parties

JWT是一个有着简单的统一表达形式的字符串:

头部(Header) 头部用于描述关于该JWT的最基本的信息,例如其类型以及签名所用的算法等。 JSON内容要经Base64 编码生成字符串成为Header。

载荷(PayLoad) payload的五个字段都是由JWT的标准所定义的。

iss: 该JWT的签发者 sub: 该JWT所面向的用户 aud: 接收该JWT的一方 exp(expires): 什么时候过期,这里是一个Unix时间戳 iat(issued at): 在什么时候签发的 后面的信息可以按需补充。 JSON内容要经Base64 编码生成字符串成为PayLoad。

签名(signature) 这个部分header与payload通过header中声明的加密方式,使用密钥secret进行加密,生成签名。 JWS的主要目的是保证了数据在传输过程中不被修改,验证数据的完整性。但由于仅采用Base64对消息内容编码,因此不保证数据的不可泄露性。所以不适合用于传输敏感数据。

JWT的Python库

独立的JWT Python库

  • itsdangerous

      JSONWebSignatureSerializer
      TimedJSONWebSignatureSerializer (可设置有效期)
    
  • pyjwt

      https://pyjwt.readthedocs.io/en/latest/
    

安装:

pip install pyjwt 

用例:

import jwt
encoded_jwt = jwt.encode({'some': 'payload'}, 'secret', algorithm='HS256')
print(encoded_jwt)
# b'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzb21lIjoicGF5bG9hZCJ9.Joh1R2dYzkRvDkqv3sygm5YyK8Gi4ShZqbhK2gxcs2U'


ret = jwt.decode(encoded_jwt, 'secret', algorithms=['HS256'])
print(ret)
# {'some': 'payload'}

密码哈希

在用户模型中,我们通常设置有一个password_hash字段,这个字段的目的是保存用户密码的哈希值,并用于验证用户在登录过程中输入的密码。 密码哈希的实现是一个复杂的话题,应该由安全专家来搞定,不过,已经有数个现成的简单易用且功能完备加密库存在了。

其中一个实现密码哈希的包是Werkzeug,当安装Flask时,你可能会在pip的输出中看到这个包,因为它是Flask的一个核心依赖项。 所以,Werkzeug已经安装在你的虚拟环境中。

pip list | grep Werk 
Werkzeug         0.15.5   

代码示例:

from werkzeug.security import generate_password_hash, check_password_hash
hash = generate_password_hash("ruiyang")
hash2 = generate_password_hash("ruiyang")

print(hash == hash2)

ret1 = check_password_hash(hash, "furuiyang")
ret2 = check_password_hash(hash, "ruiyang")
ret3 = check_password_hash(hash2, 'ruiyang')
print(ret1, ret2, ret3)

在这个例子中,通过一系列已知没有反向操作的加密操作,将密码ruiyang转换成一个长编码字符串, 这意味着获得密码哈希值的人将无法使用它逆推出原始密码。 作为一个附加手段,多次哈希相同的密码,你将得到不同的结果, 所以这使得无法通过查看它们的哈希值来确定两个用户是否具有相同的密码。 验证过程只能使用Werkzeug的第二个函数check_password_hash来完成

在业务逻辑中,整个密码哈希逻辑可以表现为在用户模型中实现为两个新的方法:

from werkzeug.security import generate_password_hash, check_password_hash

# ...

class User(db.Model):
    # ...

    def set_password(self, password):
        self.password_hash = generate_password_hash(password)

    def check_password(self, password):
        return check_password_hash(self.password_hash, password)

使用这两种方法,用户对象现在可以在无需持久化存储原始密码的条件下执行安全的密码验证。 以下是这些新方法的示例用法:

>>> u = User(username='susan', email='susan@example.com')
>>> u.set_password('mypassword')
>>> u.check_password('anotherpassword')
False
>>> u.check_password('mypassword')
True