zoukankan      html  css  js  c++  java
  • flask全栈开发8 Flask-WTF和cookie和session

    8.Flask-WTF和cookie和session

    第十章 Flask-WTF

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

    pip install flask-wtf
    

    表单验证:

    安装完Flask-WTF后。来看下第一个功能,就是用表单来做数据验证,现在有一个forms.py文件,然后在里面创建一个RegistForm的注册验证表单:

    class RegistForm(Form):
        name = StringField(validators=[length(min=4,max=25)])
        email = StringField(validators=[email()])
        password = StringField(validators=[DataRequired(),length(min=6,max=10),EqualTo('confirm')])
        confirm = StringField()
    

    在这个里面指定了需要上传的参数,并且指定了验证器,比如name的长度应该在4-25之间。email必须要满足邮箱的格式。password长度必须在6-10之间,并且应该和confirm相等才能通过验证。

    写完表单后,接下来就是regist.html文件:

        <form action="/regist/" method="POST">
            <table>
                <tr>
                    <td>用户名:</td>
                    <td><input type="text" name="name"></td>
                </tr>
                <tr>
                    <td>邮箱:</td>
                    <td><input type="email" name="email"></td>
                </tr>
                <tr>
                    <td>密码:</td>
                    <td><input type="password" name="password"></td>
                </tr>
                <tr>
                    <td>确认密码:</td>
                    <td><input type="password" name="confirm"></td>
                </tr>
                <tr>
                    <td></td>
                    <td><input type="submit" value="提交"></td>
                </tr>
            </table>
        </form>
    

    再来看视图函数regist

    @app.route('/regist/',methods=['POST','GET'])
    def regist():
        form = RegistForm(request.form)
        if request.method == 'POST' and form.validate():
            user = User(name=form.name.data,email=form.email.data,password=form.password.data)
            db.session.add(user)
            db.session.commit()
            return u'注册成功!'
        return render_template('regist.html')
    

    RegistForm传递的是request.form进去进行初始化,并且判断form.validate会返回用户提交的数据是否满足表单的验证。

    渲染模板:

    form还可以渲染模板,让你少写了一丢丢的代码,比如重写以上例子,RegistForm表单代码如下:

    class RegistForm(Form):
        name = StringField(u'用户名:',validators=[length(min=4,max=25)])
        email = StringField(u'邮箱:'validators=[email()])
        password = StringField(u'密码:',validators=[DataRequired(),length(min=6,max=10),EqualTo('confirm')])
        confirm = StringField(u'确认密码:')
    

    以上增加了第一个位置参数,用来在html文件中,做标签提示作用。

    app中的视图函数中,修改为如下:

    @app.route('/regist/',methods=['POST','GET'])
    def regist():
        form = RegistForm(request.form)
        if request.method == 'POST' and form.validate():
            user = User(name=form.name.data,email=form.email.data,password=form.password.data)
            db.session.add(user)
            db.session.commit()
            return u'注册成功!'
        return render_template('regist.html',form=form)
    

    以上唯一的不同是在渲染模板的时候传入了form表单参数进去,这样在模板中就可以使用表单form变量了。

    接下来看下regist.html文件:

    <form action="/regist/" method="POST">
        <table>
            <tr>
                <td>{{ form.name.label }}</td>
                <td>{{ form.name() }}</td>
            </tr>
            <tr>
                <td>{{ form.email.label }}</td>
                <td>{{ form.email() }}</td>
            </tr>
            <tr>
                <td>{{ form.password.label }}</td>
                <td>{{ form.password() }}</td>
            </tr>
            <tr>
                <td>{{ form.confirm.label }}</td>
                <td>{{ form.confirm() }}</td>
            </tr>
            <tr>
                <td></td>
                <td><input type="submit" value="提交"></td>
            </tr>
        </table>
    </form>
    

    Field常用参数:

    在使用Field的时候,经常需要传递一些参数进去,以下将对一些常用的参数进行解释:

    • label(第一个参数):Field的label的文本。
    • validators:验证器。
    • id:Field的id属性,默认不写为该属性名。
    • default:默认值。
    • widget:指定的html控件。

    常用Field:

    • BooleanField:布尔类型的Field,渲染出去是checkbox

    • FileField:文件上传Field。

        # forms.py
        from flask_wtf.file import FileField,FileAllowed,FileRequired
        class UploadForm(FlaskForm):
            avatar = FileField(u'头像:',validators=[FileRequired(),FileAllowed([])])
      
        # app.py
        @app.route('/profile/',methods=('POST','GET'))
        def profile():
            form = ProfileForm()
            if form.validate_on_submit():
                filename = secure_filename(form.avatar.data.filename)
                form.avatar.data.save(os.path.join(app.config['UPLOAD_FOLDER'],filename))
            return u'上传成功'
      
            return render_template('profile.html',form=form)
      
    • FloatField:浮点数类型的Field,但是渲染出去的时候是text的input。

    • IntegerField:整形的Field。同FloatField。

    • RadioField:radio类型的input。表单例子如下:

        # form.py
        class RegistrationForm(FlaskForm):
            gender = wtforms.RadioField(u'性别:',validators=[DataRequired()])
      

      模板文件代码如下:

        <tr>
            <td>
                {{ form.gender.label }}
            </td>
            <td>
                {% for gender in form.gender %}
                    {{ gender.label }}
                    {{ gender }}
                {% endfor %}
            </td>
        </tr>
      

      app.py文件的代码如下,给gender添加了choices

        @app.route('/register/',methods=['POST','GET'])
        def register():
            form = RegistrationForm()
            form.gender.choices = [('1',u'男'),('2',u'女')]
            if form.validate_on_submit():
                return u'success'
      
            return render_template('register.html',form=form)
      
    • SelectField:类似于RadioField。看以下示例:

        # forms.py
        class ProfileForm(FlaskForm):
            language = wtforms.SelectField('Programming Language',choices=[('cpp','C++'),('py','python'),('text','Plain Text')],validators=[DataRequired()])
      

      再来看app.py文件:

        @app.route('/profile/',methods=('POST','GET'))
        def profile():
            form = ProfileForm()
            if form.validate_on_submit():
                print form.language.data
                return u'上传成功'
            return render_template('profile.html',form=form)
      

      模板文件为:

        <form action="/profile/" method="POST">
            {{ form.csrf_token }}
            {{ form.language.label }}
            {{ form.language() }}
            <input type="submit">
        </form>
      
    • StringField:渲染到模板中的类型为``,并且是最基本的文本验证。

    • PasswordField:渲染出来的是一个passwordinput标签。

    • TextAreaField:渲染出来的是一个textarea

    常用的验证器:

    数据发送过来,经过表单验证,因此需要验证器来进行验证,以下对一些常用的内置验证器进行讲解:

    • Email:验证上传的数据是否为邮箱。
    • EqualTo:验证上传的数据是否和另外一个字段相等,常用的就是密码和确认密码两个字段是否相等。
    • InputRequired:原始数据的需要验证。如果不是特殊情况,应该使用InputRequired
    • Length:长度限制,有min和max两个值进行限制。
    • NumberRange:数字的区间,有min和max两个值限制,如果处在这两个数字之间则满足。
    • Regexp:自定义正则表达式。
    • URL:必须要是URL的形式。
    • UUID:验证UUID

    自定义验证字段:

    使用validate_fieldname(self,field)可以对某个字段进行更加详细的验证,如下:

    class ProfileForm(FlaskForm):
        name = wtforms.StringField('name',[validators.InputRequired()])
        def validate_name(self,field):
            if len(field.data) > 5:
                raise wtforms.ValidationError(u'超过5个字符')
    

    CSRF保护:

    在flask的表单中,默认是开启了csrf保护功能的,如果你想关闭表单的csrf保护,可以在初始化表单的时候传递csrf_enabled=False进去来关闭csrf保护。如果你想关闭这种默认的行为。如果你想在没有表单存在的请求视图函数中也添加csrf保护,可以开启全局的csrf保护功能:

    csrf = CsrfProtect()
    csrf.init_app(app)
    

    或者是针对某一个视图函数,使用csrf.protect装饰器来开启csrf保护功能。并且如果已经开启了全局的csrf保护,想要关闭某个视图函数的csrf保护功能,可以使用csrf.exempt装饰器来取消本视图函数的保护功能。

    AJAX的CSRF保护:

    AJAX中要使用csrf保护,则必须手动的添加X-CSRFTokenHeader中。但是CSRF从哪里来,还是需要通过模板给渲染,而Flask比较推荐的方式是在meta标签中渲染csrf,如下:

    <meta name="csrf-token" content="{{ csrf_token() }}">
    

    如果要发送AJAX请求,则在发送之前要添加CSRF,代码如下(使用了jQuery):

    var csrftoken = $('meta[name=csrf-token]').attr('content')
    $.ajaxSetup({
        beforeSend: function(xhr, settings) {
            if (!/^(GET|HEAD|OPTIONS|TRACE)$/i.test(settings.type) && !this.crossDomain) {
                xhr.setRequestHeader("X-CSRFToken", csrftoken)
            }
        }
    })
    

    第十一章 cookie和session

    1. cookie:在网站中,http请求是无状态的。也就是说即使第一次和服务器连接后并且登录成功后,第二次请求服务器依然不能知道当前请求是哪个用户。cookie的出现就是为了解决这个问题,第一次登录后服务器返回一些数据(cookie)给浏览器,然后浏览器保存在本地,当该用户发送第二次请求的时候,就会自动的把上次请求存储的cookie数据自动的携带给服务器,服务器通过浏览器携带的数据就能判断当前用户是哪个了。cookie存储的数据量有限,不同的浏览器有不同的存储大小,但一般不超过4KB。因此使用cookie只能存储一些小量的数据。
    2. session: session和cookie的作用有点类似,都是为了存储用户相关的信息。不同的是,cookie是存储在本地浏览器,session是一个思路、一个概念、一个服务器存储授权信息的解决方案,不同的服务器,不同的框架,不同的语言有不同的实现。虽然实现不一样,但是他们的目的都是服务器为了方便存储数据的。session的出现,是为了解决cookie存储数据不安全的问题的。
    3. cookie和session结合使用:web开发发展至今,cookiesession的使用已经出现了一些非常成熟的方案。在如今的市场或者企业里,一般有两种存储方式:
      • 存储在服务端:通过cookie存储一个session_id,然后具体的数据则是保存在session中。如果用户已经登录,则服务器会在cookie中保存一个session_id,下次再次请求的时候,会把该session_id携带上来,服务器根据session_idsession库中获取用户的session数据。就能知道该用户到底是谁,以及之前保存的一些状态信息。这种专业术语叫做server side session。存储在服务器的数据会更加的安全,不容易被窃取。但存储在服务器也有一定的弊端,就是会占用服务器的资源,但现在服务器已经发展至今,一些session信息还是绰绰有余的。
      • session数据加密,然后存储在cookie中。这种专业术语叫做client side sessionflask采用的就是这种方式,但是也可以替换成其他形式。

    flask中使用cookie和session

    cookies:在Flask中操作cookie,是通过response对象来操作,可以在response返回之前,通过response.set_cookie来设置,这个方法有以下几个参数需要注意:

      • key:设置的cookie的key。
      • value:key对应的value。
      • max_age:改cookie的过期时间,如果不设置,则浏览器关闭后就会自动过期。
      • expires:过期时间,应该是一个datetime类型。
      • domain:该cookie在哪个域名中有效。一般设置子域名,比如cms.example.com
      • path:该cookie在哪个路径下有效。
    1. session:Flask中的session是通过from flask import session。然后添加值key和value进去即可。并且,Flask中的session机制是将session信息加密,然后存储在cookie中。专业术语叫做client side session

    session 使用加密sessionid存储在cookie中,返回服务器查出用户信息,着用假如黑客破解用户id进行转账,想要破解sessionid转账给另一个用户,需要破解的时间成本太高,所以目前session存储还是安全的

  • 相关阅读:
    网站性能在线评估
    如何测试电梯/伞/桌子/笔?
    apk反编译查看源码
    Jmeter(四)-断言/检查点
    【转】Jmeter(三)-简单的HTTP请求(非录制)
    【转】Jmeter(二)-使用代理录制脚本
    [转]Jmeter(一)-精简测试脚本
    CentOS 安装以及配置Apache php mysql
    centOS静态ip设置
    免费DDOS攻击测试工具大合集
  • 原文地址:https://www.cnblogs.com/sky-ai/p/12830549.html
Copyright © 2011-2022 走看看