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存储还是安全的

  • 相关阅读:
    常见寻找OEP脱壳的方法
    Windows内核原理系列01
    HDU 1025 Constructing Roads In JGShining's Kingdom
    HDU 1024 Max Sum Plus Plus
    HDU 1003 Max Sum
    HDU 1019 Least Common Multiple
    HDU 1018 Big Number
    HDU 1014 Uniform Generator
    HDU 1012 u Calculate e
    HDU 1005 Number Sequence
  • 原文地址:https://www.cnblogs.com/sky-ai/p/12830549.html
Copyright © 2011-2022 走看看