前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Python Web - Flask笔记8

Python Web - Flask笔记8

作者头像
YingJoy_
发布2018-08-02 15:52:59
1.2K0
发布2018-08-02 15:52:59
举报
文章被收录于专栏:应兆康的专栏应兆康的专栏

58. CSRF攻击与防御

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

CSRF攻击原理

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

防御CSRF攻击

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

代码语言:javascript
复制
from flask_wtf import CSRFProtect
CSRFProtect(app)

表单页面添加:

代码语言:javascript
复制
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}" >

AJAX的CSRF保护

在AJAX中要使用csrf保护,则必须手动添加x-CSRFToken到Header中,但是csrf还是需要在模板中渲染,Flask推荐使用meta标签来渲染csrf

代码语言:javascript
复制
<meta name="csrf_token" content="{{ csrf_token() }}">

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

代码语言:javascript
复制
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)
        }
    }
})

把csrf_token放在meta标签中也更容易继承。

封装AJAX

先在页面中添加meta标签

代码语言:javascript
复制
<meta name="csrf_token" content="{{ csrf_token() }}">

文件: yajax.js

代码语言:javascript
复制
// 对jquery的ajax的封装

'use strict';
var yajax = {
    'get':function(args) {
        args['method'] = 'get';
        this.ajax(args);
    },
    'post':function(args) {
        args['method'] = 'post';
        this.ajax(args);
    },
    'ajax':function(args) {
        // 设置csrftoken
        this._ajaxSetup();
        $.ajax(args);
    },
    '_ajaxSetup': function() {
        $.ajaxSetup({
            'beforeSend':function(xhr,settings) {
                if (!/^(GET|HEAD|OPTIONS|TRACE)$/i.test(settings.type) && !this.crossDomain) {
                    var csrftoken = $('meta[name=csrf-token]').attr('content');
                    xhr.setRequestHeader("X-CSRFToken", csrftoken)
                }
            }
        });
    }
};
代码语言:javascript
复制
// 整个文档都加载完毕后才会执行这个函数
$(function () {
    $('#submit').click(function (event) {
        // 阻止默认的提交表单的行为
        event.preventDefault();
        var email = $('input[name=email]').val();
        var password = $('input[name=password]').val();

        yajax.post({
            'url': '/login/',
            'data': {
                'email': email,
                'password': password
            },
            'success': function (data) {
                console.log(data);
            },
            'fail': function (error) {
                console.log(error);
            }
        });
    });
});

59. Local线程隔离对象

代码语言:javascript
复制
from werkzeug.local import Local
local = Local()
local.session / local.g / local.request # 这三个都是线程隔离的

在Flask中。类似request的对象,其实是绑定到了werkzeug.local.Local对象上。这样,在多线程中,每个对象都是隔离的。说白了就是,不同用户访问时,数据是独立的。

Thread Local对象

只要满足绑定到这个对象上的属性,在每个线程中都是隔离的,那么它就叫Thread Local对象。

##60. app上下文和request上下文

应用上下文和请求上下文都是存放到一个LocalStack的栈中。和应用app相关的操作就必须要用到应用上下文,比如通过current_app获取当前的这个app。和请求相关的操作就必须用到请求上下文,比如使用url_for反转视图函数。 1. 在视图函数中,不用担心上下文的问题。因为视图函数要执行,那么肯定是通过访问url的方式执行的,那么这种情况下,Flask底层就已经自动的帮我们把请求上下文和应用上下文都推入到了相应的栈中。 2. 如果想要在视图函数外面执行相关的操作,比如获取当前的app(current_app),或者是反转url,那么就必须要手动推入相关的上下文: * 手动推入app上下文: python # 第一种方式: app_context = app.app_context() app_context.push() # 第二种方式: with app.app_context(): print(current_app) * 手动推入请求上下文:推入请求上下文到栈中,会首先判断有没有应用上下文,如果没有那么就会先推入应用上下文到栈中,然后再推入请求上下文到栈中: python with app.test_request_context(): print(url_for('my_list'))

为什么上下文需要放在栈中:

  1. 应用上下文:Flask底层是基于werkzeug,werkzeug是可以包含多个app的,所以这时候用一个栈来保存。如果你在使用app1,那么app1应该是要在栈的顶部,如果用完了app1,那么app1应该从栈中删除。方便其他代码使用下面的app。
  2. 如果在写测试代码,或者离线脚本的时候,我们有时候可能需要创建多个请求上下文,这时候就需要存放到一个栈中了。使用哪个请求上下文的时候,就把对应的请求上下文放到栈的顶部,用完了就要把这个请求上下文从栈中移除掉。

62. 保存全局对象的g对象:

g对象是在整个Flask应用运行期间都是可以使用的。并且他也是跟request一样,是线程隔离的。这个对象是专门用来存储开发者自己定义的一些数据,方便在整个Flask程序中都可以使用。一般使用就是,将一些经常会用到的数据绑定到上面,以后就直接从g上面取就可以了,而不需要通过传参的形式,这样更加方便。

代码语言:javascript
复制
g.username = username

63. 常用的钩子函数:

在Flask中钩子函数是使用特定的装饰器装饰的函数。为什么叫做钩子函数呢,是因为钩子函数可以在正常执行的代码中,插入一段自己想要执行的代码。那么这种函数就叫做钩子函数。(hook) 1. before_first_request:Flask项目第一次部署后会执行的钩子函数。

  1. before_request:请求已经到达了Flask,但是还没有进入到具体的视图函数之前调用。一般这个就是在视图函数之前,我们可以把一些后面需要用到的数据先处理好,方便视图函数使用。
  2. context_processor:使用这个钩子函数,必须返回一个字典。这个字典中的值在所有模版中都可以使用。这个钩子函数的函数是,如果一些在很多模版中都要用到的变量,那么就可以使用这个钩子函数来返回,而不用在每个视图函数中的render_template中去写,这样可以让代码更加简洁和好维护。 @app.context_processor def context_processor(): return {"username":"ying"}
  3. errorhandler:在发生一些异常的时候,比如404错误,比如500错误。那么如果想要优雅的处理这些错误,就可以使用errorhandler来出来。需要注意几点:
    • 在errorhandler装饰的钩子函数下,记得要返回相应的状态码。
    • 在errorhandler装饰的钩子函数中,必须要写一个参数,来接收错误的信息,如果没有参数,就会直接报错。
    • 使用flask.abort可以手动的抛出相应的错误,比如开发者在发现参数不正确的时候可以自己手动的抛出一个400错误。 示例代码如下:
代码语言:javascript
复制
@app.errorhandler(404)
def page_not_found(error):
    return render_template('404.html'),404

64. 信号机制及使用场景

Flask信号,安装blinker,blinker是一个第三方插件。一般用于记录日志,如用户一登陆就把相关信息写入文件。

代码语言:javascript
复制
pip install blinker

使用信号分为3步,第一是定义一个信号,第二是监听一个信号,第三是发送一个信号。以下将对这三步进行讲解:

  1. 定义信号:定义信号需要使用到blinker这个包的Namespace类来创建一个命名空间。比如定义一个在访问了某个视图函数的时候的信号。示例代码如下: # Namespace的作用:为了防止多人开发的时候,信号名字冲突的问题 from blinker import Namespace mysignal = Namespace() visit_signal = mysignal.signal('visit-signal')
  2. 监听信号:监听信号使用singal对象的connect方法,在这个方法中需要传递一个函数,用来接收以后监听到这个信号该做的事情。示例代码如下: python def visit_func(sender,username): print(sender) print(username) mysignal.connect(visit_func)
  3. 发送信号:发送信号使用singal对象的send方法,这个方法可以传递一些其他参数过去。示例代码如下: python mysignal.send(username='zhiliao')

65. Flask内置的信号:

  1. template_rendered:模版渲染完成后的信号。 def template_rendered_func(sender, template, context): print('sender', sender) print('template', template) print('context', context) template_rendered.connect(template_rendered_func) @app.route('/') def index(): return render_template('index.html') before_render_template:模版渲染之前的信号。
  2. request_started:模版开始渲染。
  3. request_finished:模版渲染完成。
  4. request_tearing_down:request对象被销毁的信号。
  5. got_request_exception:视图函数发生异常的信号。一般可以监听这个信号,来记录网站异常信息。
  6. appcontext_tearing_down:app上下文被销毁的信号。
  7. appcontext_pushed:app上下文被推入到栈上的信号。
  8. appcontext_popped:app上下文被推出栈中的信号
  9. message_flashed:调用了Flask的flashed方法的信号。
  • 注: 以下方法可以获取ip地址 from flask import request ip = request.remote_addr ​

66. Restful API规范

Restful API 是用于前后端通信的一套规范,这个规范可以使得前后端开发更加轻松。

协议

采用http或https协议

数据传输格式

数据之间传输的格式应该都是用json格式,而不使用xml

url链接

url链接中,不能有动词,只能由名词,并且名词如果位复数,就要在后面加s

HTTP请求的方法

  1. GET:从服务器上获取资源
  2. POST:在服务器上新创建一个资源
  3. PUT:在服务器上更新资源(客户端提供所有改变后的数据)
  4. PATCH在服务器上更新资源(客户端只提供需要改变的属性)
  5. DELETE:从服务器上删除资源

状态码

状态码

原生描述

描述

200

ok

服务器成功响应客户端请求

400

invalid request

用户发出的请求有误,服务器没有进行新建或修改数据的操作

401

unauthorized

用户没有权限访问这个请求

403

forbidden

因为某些原因禁止访问这个请求

404

not found

用户发送请求的url不存在

406

not acceptable

用户请求不被服务器接收(比如服务器期望客户端发送某个字段,但是客户端没有发送)

500

internal server error

内部服务器错误,代码错误

67. Flask-Restful

安装:

Flask-Restful需要在Flask 0.8以上的版本,在Python2.6或者Python3.3上运行。通过pip install flask-restful即可安装。

基本使用:

  1. flask_restful中导入Api,来创建一个api对象。
  2. 写一个视图函数,让他继承自Resource,然后在这个里面,使用你想要的请求方式来定义相应的方法,比如你想要将这个视图只能采用post请求,那么就定义一个post方法。
  3. 使用api.add_resource来添加视图与url。 示例代码如下:
代码语言:javascript
复制
class LoginView(Resource):
    def post(self,username=None):
        return {"username":"ying"}

api.add_resource(LoginView,'/login/<username>/','/regist/')

注意事项: * 如果你想返回json数据,那么就使用flask_restful,如果你是想渲染模版,那么还是采用之前的方式,就是app.route的方式。 * url还是跟之前的一样,可以传递参数。也跟之前的不一样,可以指定多个url。 * endpoint是用来给url_for反转url的时候指定的。如果不写endpoint,那么将会使用视图的名字的小写来作为endpoint。

参数验证:

Flask-Restful插件提供了类似WTForms来验证提交的数据是否合法的包,叫做reqparse。以下是基本用法: ​python parser = reqparse.RequestParser() parser.add_argument('username',type=str,help='请输入用户名') args = parser.parse_args() add_argument可以指定这个字段的名字,这个字段的数据类型等。以下将对这个方法的一些参数做详细讲解: 1. default:默认值,如果这个参数没有值,那么将使用这个参数指定的值。 2. required:是否必须。默认为False,如果设置为True,那么这个参数就必须提交上来。 3. type:这个参数的数据类型,如果指定,那么将使用指定的数据类型来强制转换提交上来的值。 3. choices:选项。提交上来的值只有满足这个选项中的值才符合验证通过,否则验证不通过。 4. help:错误信息。如果验证失败后,将会使用这个参数指定的值作为错误信息。 5. trim:是否要去掉前后的空格。

其中的type,可以使用python自带的一些数据类型,也可以使用flask_restful.inputs下的一些特定的数据类型来强制转换。比如一些常用的: 1. url:会判断这个参数的值是否是一个url,如果不是,那么就会抛出异常。 2. regex:正则表达式。 3. date:将这个字符串转换为datetime.date数据类型。如果转换不成功,则会抛出一个异常。

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 58. CSRF攻击与防御
    • CSRF攻击原理
      • 防御CSRF攻击
        • AJAX的CSRF保护
          • 封装AJAX
      • 59. Local线程隔离对象
        • Thread Local对象
          • 为什么上下文需要放在栈中:
          • 62. 保存全局对象的g对象:
          • 63. 常用的钩子函数:
          • 64. 信号机制及使用场景
          • 65. Flask内置的信号:
          • 66. Restful API规范
            • 协议
              • 数据传输格式
                • url链接
                  • HTTP请求的方法
                    • 状态码
                    • 67. Flask-Restful
                      • 安装:
                        • 基本使用:
                          • 参数验证:
                          领券
                          问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档