详解Django的CSRF认证实现
什么是 CSRF
CSRF, Cross Site Request Forgery, 跨站点伪造请求。举例来讲,某个恶意的网站上有一个指向你的网站的链接,如果某个用户已经登录到你的网站上了,那么当这个用户点击这个恶意网站上的那个链接时,就会向你的网站发来一个请求,你的网站会以为这个请求是用户自己发来的,其实呢,这个请求是那个恶意网站伪造的。
1.csrf原理
csrf要求发送post,put或delete请求的时候,是先以get方式发送请求,服务端响应时会分配一个随机字符串给客户端,客户端第二次发送post,put或delete请求时携带上次分配的随机字符串到服务端进行校验
2.Django中的CSRF中间件
首先,我们知道Django中间件作用于整个项目。
在一个项目中,如果想对全局所有视图函数或视图类起作用时,就可以在中间件中实现,比如想实现用户登录判断,基于用户的权限管理(RBAC)等都可以在Django中间件中来进行操作
Django内置了很多中间件,其中之一就是CSRF中间件
1 2 3 4 5 6 7 8 9 10 |
|
上面第四个就是Django内置的CSRF中间件
3.Django中间件的执行流程
Django中间件中最多可以定义5个方法
- process_request
- process_response
- process_view
- process_exception
- process_template_response
Django中间件的执行顺序
1.请求进入到Django后,会按中间件的注册顺序执行每个中间件中的process_request方法
如果所有的中间件的process_request方法都没有定义return语句,则进入路由映射,进行url匹配
否则直接执行return语句,返回响应给客户端
2.依次按顺序执行中间件中的process_view方法
如果某个中间件的process_view方法没有return语句,则根据第1步中匹配到的URL执行对应的视图函数或视图类
如果某个中间件的process_view方法中定义了return语句,则后面的视图函数或视图类不会执行,程序会直接返回
3.视图函数或视图类执行完成之后,会按照中间件的注册顺序逆序执行中间件中的process_response方法
如果中间件中定义了return语句,程序会正常执行,把视图函数或视图类的执行结果返回给客户端
否则程序会抛出异常
4.程序在视图函数或视图类的正常执行过程中
如果出现异常,则会执行按顺序执行中间件中的process_exception方法
否则process_exception方法不会执行
如果某个中间件的process_exception方法中定义了return语句,则后面的中间件中的process_exception方法不会继续执行了
5.如果视图函数或视图类中使用render方法来向客户端返回数据,则会触发中间件中的process_template_response方法
4.Django CSRF中间件的源码解析
Django CSRF中间件的源码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 |
|
从上面的源码中可以看到,CsrfViewMiddleware中间件中定义了process_request,process_view和process_response三个方法
先来看process_request方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
|
从Django项目配置文件夹中读取 CSRF_USE_SESSIONS 的值,如果获取成功,则 从session中读取CSRF_SESSION_KEY的值 ,默认为 '_csrftoken' ,如果没有获取到 CSRF_USE_SESSIONS 的值,则从发送过来的请求中获取 CSRF_COOKIE_NAME 的值,如果没有定义则返回None。
再来看process_view方法
在process_view方法中,先检查视图函数是否被 csrf_exempt 装饰器装饰,如果视图函数没有被csrf_exempt装饰器装饰,则程序继续执行,否则返回None。接着从request请求头中或者cookie中获取携带的token并进行验证,验证通过才会继续执行与URL匹配的视图函数,否则就返回 403 Forbidden 错误。
实际项目中,会在发送POST,PUT,DELETE,PATCH请求时,在提交的form表单中添加
1 |
|
即可,否则会出现403的错误
5.csrf_exempt装饰器和csrf_protect装饰器
5.1 基于Django FBV
在一个项目中,如果注册起用了 CsrfViewMiddleware 中间件,则项目中所有的视图函数和视图类在执行过程中都要进行CSRF验证。
此时想使某个视图函数或视图类不进行CSRF验证,则可以使用 csrf_exempt 装饰器装饰不想进行CSRF验证的视图函数
1 2 3 4 5 |
|
也可以把csrf_exempt装饰器直接加在URL路由映射中,使某个视图函数不经过CSRF验证
1 2 3 4 5 6 7 8 |
|
同样的,如果在一个Django项目中,没有注册起用 CsrfViewMiddleware 中间件,但是想让某个视图函数进行CSRF验证,则可以使用 csrf_protect 装饰器
csrf_protect装饰器的用法跟csrf_exempt装饰器用法相同 ,都可以加上视图函数上方装饰视图函数或者在URL路由映射中直接装饰视图函数
1 2 3 4 5 |
|
或者
1 2 3 4 5 6 7 8 |
|
5.1 基于Django CBV
上面的情况是基于Django FBV的,如果是基于Django CBV,则不可以直接加在视图类的视图函数中了
此时有三种方式来对Django CBV进行CSRF验证或者不进行CSRF验证
方法一,在视图类中定义dispatch方法,为dispatch方法加csrf_exempt装饰器
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
|
方法二:为视图类上方添加装饰器
1 2 3 4 5 6 7 8 9 10 11 12 13 |
|
方式三:在url.py中为类添加装饰器
1 2 3 4 5 6 |
|
csrf_protect装饰器的用法跟上面一样