flask-wtf使用笔记

一、WTForms

这个库一般有两个作用:

  1. 做表单验证
  2. 模版渲染

表单验证

  1. 自定义一个表单类,继承 wtforms.Form;
  2. 定义好需要验证的字段,字段的名字必须和表单中需要验证的字段名的 name 保持一致;
  3. 在需要验证的字段上指定好具体的字段类型,
  4. 在相关的字段上指定好验证器。
  5. 以后只需要使用这个表单类的对象,并且把 request.for传递给这个表单类,调用对象.validate()
    如果返回 True,表示用户输入合法,
    如果验证错误,可以通过 表单类对象.errors来获取错误信息

示例代码如下:
项目代码:

# app.py
from flask import Flask, render_template, request
from wtforms import Form, StringField
from wtforms.validators import Length, EqualTo

app = Flask(__name__)


class RegisterForm(Form):
    username = StringField(validators=[Length(min=3, max=10,message='用户名长度应在3-10个字符之间')])
    password = StringField(validators=[Length(min=6, max=10,message='密码应该在 6-10个字符之间')])
    re_password = StringField(validators=[Length(min=6, max=10,message='重复密码应该在 6-10个字符之间'), EqualTo('password',message='重复密码和密码不一致!')])

@app.route('/register/', methods=['GET', 'POST'])
def register():
    if request.method == 'GET':
        return render_template('register.html')
    if request.method == 'POST':
        
        # 用 WTForm 做表单验证
        form = RegisterForm(request.form)
        if form.validate():
            return 'success'
        else:
            print(form.errors)
            return form.errors



html代码:




    
    注册页面


    

用户注册

用户名:
密码:
重复密码:

常用验证器

  • Email: 邮箱验证。

  • EqualTo: 验证和其他字段是否一致,常用做确认密码验证。

  • InputRequired:原始数据需要验证。

  • Length: 长度限制。

  • NumberRange: 数字区间,有 min 和 max 两个值进行限制。

  • Regexp: 只定义正则表达式。

  • URL: 必须是 URL 的形式。

  • UUID: 验证 UUID。

自定义验证字段

如果需要对表单中的字段做更细化的验证,可以使用自定义验证器来验证,步骤如下:

  1. 定义一个方法,方法的名字规则是: validate_字段名(self,filed)
  2. 在方法中使用field.data可以获取到这个字段的具体值。
  3. 如果条件满足,可以什么都不做,如果验证失败,那么应该抛出一个wtforms.validators.ValidationError(message)异常,其中 message是验证异常的消息提示。
    示例代码如下:
captcha = StringField(validators=[Length(4, 4)])

    # 自定义验证字段
    def validate_captcha(self, field):
        # 此处模拟验证码是 1234
        if field.data != '1234':
            raise ValidationError('输入的验证码不正确')

使用 WTForms 渲染模版

步骤:

  1. 示例化表单类,传递到模版文件中,例如 form = LoginForm()
  2. 在模版中使用 {{模版对象.字段名}}生成模版,可以通过 模版对象.字段名.label获取设置好显示的字段名,在定义每个字段的时候,可以传递label=xxx,可以在渲染模版的时候用做字段名

二、flask-wtf 笔记

Flask-WTF 是简化了 WTForms 操作的一个第三方库。WRFroms表单的两个主要功能是验证用户提交数据的合法性以及渲染模版。当然还包括一些其他功能。CSRF保护,文件上传等。
安装Flask-WTF 默认也会安装 WTForms,安装命令如下:

pip install flask-wtf

1.表单验证(同wtforms,增加了部分验证器)

2.模版渲染(使用方式同上)


文件上传笔记

  1. 在文件上传的表单中,需要指定 enctype="multipart/form-data" 才能上传文件

  2. 在后台如果需要获取到文件,需使用 request.files.get('xxx') 来获取文件,xxx是指定文件的 name 属性。

  3. 保存文件之前,先使用 werkzeug.utils.secute_filename 来对上传的文件名做过滤。这样才能保证不会有安全问题。

  4. 可以调用 文件.sava()方法保存文件,需要传递 保存文件的文件名(路径 + 文件名)。

  5. 从服务器上读取一个文件,应该定义一个url与视图函数,来获取指定文件。在这个视图函数中,使用send_form_directory(文件的目录,文件名)来获取指定文件。
    示例代码如下:

@app.route('/upload', methods=['GET', 'POST'])
def upload():
    if request.method == 'GET':
        return render_template('upload.html')
    else:
        # 获取描述信息
        desc = request.form.get('desc')
        # 获取文件
        avatar = request.files.get('avatar')
        # 可以通过 获取到的文件的 save方法保存文件,filename 属性可以获取到文件的文件名
        # 为了安全起见,包装文件名
        filename = secure_filename(avatar.filename)
        avatar.save(os.path.join(UPLOAD_DIR, filename))
        return '上传成功'

@app.route('/images/')
def get_image(filename):
    return send_from_directory(UPLOAD_DIR, filename)  # 此处的UPLOAD_DIR为上传文件的目录字符串

使用flask-wtf验证上传的文件

步骤:

  1. 定义表单的时候,对文件的字段,需要采用FileField这个类型。
  2. 验证器应该从flask_tf.file中导入.
    • FileRequired 验证文件是否为空
    • FileAllowed 指定文件格式
      例如:
    class UploadForm(Form):
    avatar = FileField(
        label='头像',
        validators=[
            FileRequired(message='您没有选择文件'),
            FileAllowed(upload_set=['jpg', 'png', 'gif', 'jpeg'], message='您上传的文件不符合规范')
        ]
    )
    desc = StringField(label='描述', validators=[InputRequired(message='您没有输入描述')])
    
  3. 在视图文件中,使用werkzeug.datastructures.CombinedMultiDict()方法
    把表单和文件结合在一起,然后初始化表单对象。
    示例如下:
# 利用 flask-wtf 验证文件上传
@app.route('/upload', methods=['GET', 'POST'])
def upload():
    if request.method == 'GET':
        upload_form = UploadForm()
        return render_template('upload_with_flask-wtf.html', form=upload_form)
    else:
        form = UploadForm(CombinedMultiDict([request.form, request.files]))  # 把表单和上传的文件共同组合为 form
        if form.validate():
            # 获取描述信息
            desc = request.form.get('desc')
            # 获取文件
            avatar = request.files.get('avatar')
            # 可以通过 获取到的文件的 save方法保存文件,filename 属性可以获取到文件的文件名
            # 为了安全起见,包装文件名
            filename = secure_filename(avatar.filename)
            avatar.save(os.path.join(UPLOAD_DIR, filename))
            return '上传成功'
        else:
            return form.errors


预防CSRF攻击

基础知识

1. cookie

  • 解决http无状态的问题
  • 存储大小有限 一般不超过4K
  • 存储在本地浏览器
  • 有有效期,服务器设置,浏览器清除
  • 有域名的概念

flask操作cookie

  1. 设置cookie
    设置cookie应该在Response的对象上设置,通过flask.Response对象的set_cookie方法
  1. 删除cookie
    通过flask.Response对象的delete_cookie方法

  2. 设置cookie有效期
    在调用Response.set_cookie()的时候,可以通过指定参数来设置 cookie 的有效期

  • max_age参数:指定一个距离现在时间的秒数,在指定的时候后,cookie到期;注意:在ie8以下的浏览器不支持这个
  • expires参数:datatime.datatime类型的时间,注意:这个时间是格林尼治时间,比北京时间晚8个小时

如果没有设置这两个参数都没有设置,cookie会在会话结束(关闭浏览器后)后到期

示例代码:

@app.route('/')
def index():
    resp = Response('主页')
    # 设置 cookie
    # expires = datetime(2019, 10, 28, 0, 0, 0)
    # resp.set_cookie(key='username', value='ck', expires=expires,max_age=60)
    resp.set_cookie(key='username', value='ck')
    return resp
  1. 设置cookie有效域名
    cookie默认是只能在主域名下使用,如果要在子域名下使用cookie,需要在设置cookie的时候,指定domain参数,这样子域名才能使用cookie信息。例如:
resp.set_cookie(key='username', value='ck', domain='.ck.com')

2. session

  1. session 的概念
    session:session和cookie的作用类似,都是为了存储用户相关的信息,不同的是,cookie是存储在本地浏览器,session是一个思路/概念,一个服务器存储授权信息的解决方案,不同的服务器,不同的框架,不同的语言有不同的实现,虽然实现不一样,但是他们的目的都是服务器为了方便存储数据的。session的出现,是为了解决cookie存储数据不安全。

  2. cookie和session结合使用

  • 存储在服务端: 通过cookie存储一个session_id,然后具体的数据则是保存在session中。
  • 将session数据加密,然后存储在cookie中。

Flask操作session

  1. 设置session:
    设置session在session对象上进行设置的,通过"flask.session"对象上的一个"session.setdefault()"方法或者字典来进行设置session信息
# one
session.setdefault("name","angle")
# two
session["name"] = "angle"
  1. 获取session:
    通过"session.get()"方法获取
name = session.get("name")
  1. 删除指定session:
    通过“session.pop()”方法删除指定的session值
# 1.删除指定session
session.pop("name")
# 2. 删除指定 session
del session['name']
  1. 清除所有的session值
# 清除所有session
session.clear()
  1. 设置session有效期:
    如果没有配置session的有效期,默认是浏览器关闭之后,结束session,
    通过配置config进行配置,默认有效期为31天
from datetime import timedelta
# 设置会话有效期时间:两个小时以后会话有效期时间过期
app.config['PERMANENT_SESSION_LIFETIME'] = timedelta(hours=2)
# 设置session的过期时间,permanent:持久性,默认时间为一个月
session.permanent = True
**注意:**使用session需要配置SECRET_KEY

```python
import os
# os.unrandom(n):产生24位的随机数
app.config["SECRET_KEY"] = os.urandom(24)
```

CSRF 攻击与防御

csrf攻击概述:

CSRF(Cross Site Request Forgery,跨站请求伪造)是一种网络的攻击方式。

csrf攻击原理:

网站是通过cookie来实现登录功能的。而cookie只要存在浏览器中,那么浏览器在访问这个cookie的服务器的时候,就会自动的携带cookie信息到服务器上去。那么这时候就存在一个漏洞了,如果你访问了一个别有用心或病毒网站,这个网站可以在网页源代码中插入js代码,使用js代码给其他服务器发送请求(比如ICBC的转账请求)。那么因为在发送请求的时候,浏览器会自动的把cookie发送给对应的服务器,这时候响应的服务器(比如ICBC网站),就不知道这个请求时是伪造的,就被欺骗过去了,从而达到在用户不知情的情况下,给某个服务器发送了一个请求(比如转账)。

防御csrf攻击

csrf攻击的要点就是在向服务器发送请求的时候,相应的 cookie会自动发送给对应的服务器。造成服务器不知道这个请求是用户发起的还是伪造的。这时候,我们可以在用户媒体访问有表单页面的时候,在网页源代码中加入一个随机字符串叫做 csrf_token,在 cookie 中同样也加入一个相同的 csrf_token 字符串。以后在给服务器发送请求的时候,必须在 body 中以及 cookie 中都携带了 csrf_token,服务器只有监测到 cookie 中的 csrf_token 和 body 中的 token 都相同,才认为这个请求是正常的,否则就是伪造的。

你可能感兴趣的:(flask-wtf使用笔记)