Kommmy Blog


  • 首页

  • 分类

  • 归档

  • 标签

浅谈django项目中前后端分离csrf传递

发表于 2018-10-23 | 分类于 编程

前言

回想起来以后有很久没有用到django提供自带的模板功能了,目前公司的项目基本都采用react+web后端的模式,这样做也相对灵活但是也会出现一些问题,比如如何处理csrf以及登录认证的问题。

CSRF

在说之前,先整体屡一下csrf_token生成的过程。
首先在中间件的配置中加入django.middleware.csrf.CsrfViewMiddleware,没有这个的话就没有下面的一切了。
这个中间件在process_view中主要是用来生成csrf_token,上来会从cookie中获取,如果cookie中没有,会自动生成一个放到request.META[“CSRF_COOKIE”],源代码如下

1
2
3
4
5
6
7
8
9
10
try:
csrf_token = _sanitize_token(
request.COOKIES[settings.CSRF_COOKIE_NAME])
# Use same token next time
request.META['CSRF_COOKIE'] = csrf_token
except KeyError:
csrf_token = None
# Generate token and store it in the request, so it's
# available to the view.
request.META["CSRF_COOKIE"] = _get_new_csrf_key()

这里注意,如果是POST方法,且cookie中没有csrf_token的话,那后面的逻辑就会直接报403,提示csrf校验失败,所以上来必须是GET请求,让cookie先种下去才可以。
接下来看process_response,源代码如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
if request.META.get("CSRF_COOKIE") is None:
return response

if not request.META.get("CSRF_COOKIE_USED", False):
return response

# Set the CSRF cookie even if it's already set, so we renew
# the expiry timer.
response.set_cookie(settings.CSRF_COOKIE_NAME,
request.META["CSRF_COOKIE"],
max_age=settings.CSRF_COOKIE_AGE,
domain=settings.CSRF_COOKIE_DOMAIN,
path=settings.CSRF_COOKIE_PATH,
secure=settings.CSRF_COOKIE_SECURE,
httponly=settings.CSRF_COOKIE_HTTPONLY
)

程序会判断CSRF_COOKIE与CSRF_COOKIE_USED是否为空,CSRF_COOKIE这个不用多说,因为有前面process_view的生成,所以正常情况下都是存在的,但是CSRF_COOKIE_USED是怎么设置的呢?
答案在django.middleware.csrf.get_token中。这个函数就做了2个事情,一个是将CSRF_COOKIE_USED设置为True,一个是返回request.META.get(“CSRF_COOKIE”, None)。
所以如果你想生成csrf_token,前置条件是必须要调用get_token(或者rotate_token)。
总结一下:
1.必须调用get_token,只有调用它,返回才会设置set-cookie把csrf_token种进去
2.前端需要通过form表单或者HTTP_X_CSRFTOKEN头把csrf_token传到后端,然后后端用传过来的值和cookie的值比对,一致就通过。

接下来说说怎么与前端结合,因为传统模板的模式下,csrf_token会自动渲染进页面,js可以直接拿到页面的值,但是在前后端分离的情况下我们应该怎么做呢?
1.先在后端写一个生成csrf_token的接口

1
2
3
def get_token(request):
token = django.middleware.csrf.get_token(request)
return JsonResponse({'token': token})

2.前端去请求这个接口,拿到token,保存到cookie中,然后post请求需要csrf_token时,直接从cookie中获取即可
3.在后端的views中要加入ensure_csrf_cookie装饰器,这个装饰器的作用就是确保这次返回的时候,回把csrf_token再成功的种回去,刷新过期时间,其实如果不加的话并不影响校验。
中间件会去验证前端传过来的token和META cookie中的token是否一致,源码逻辑如下

1
2
3
4
5
6
7
if request_csrf_token == "":
# Fall back to X-CSRFToken, to make things easier for AJAX,
# and possible for PUT/DELETE.
request_csrf_token = request.META.get('HTTP_X_CSRFTOKEN', '')

if not constant_time_compare(request_csrf_token, csrf_token):
return self._reject(request, REASON_BAD_TOKEN)

这样就可以完成前后端分离模式下的csrf_token的传递。

另外再多说一句登录认证的事情,因为前后端分离,在开发接口前端工程师通常和后端工程师不在一个域下面,所以session-cookie方式登录就不好使了,需要约定token来实现登录认证。
另外一种方式是jwt认证(JSON Web Tokens),前端用账号密码换取jwt,之后的所有请求都带着jwt,即可实现登录认证。

快速了解Python中的协程

发表于 2018-05-25 | 分类于 编程

1.概要

相信写过异步IO编程的人肯定对协程不陌生,那么在Python中有没有简单的方式实现协程呢?
从Python 3.5开始引入了新的语法async和await,可以非常简单的实现协程同时代码更简洁易读。
现在让我们来对比一下使用协程和不使用协程的区别!

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#coding=utf-8
import asyncio
import types
import aiohttp
import time
async def do_some_work(x):
print("Waiting " + str(x))
time.sleep(x)
print(f"{x} seconds")
return x


coros = [do_some_work(4), do_some_work(3)]
loop = asyncio.get_event_loop()
result = loop.run_until_complete(asyncio.wait(coros))

上面是一段没有用到协程的代码,运行代码后会发现,虽然函数已经被声明为协程,但是函数内部没有await,所以不会主动切换函数,程序依然是顺序执行,第一个函数sleep了4秒,第二个函数sleep了3秒,总共7秒执行完毕。

让我们改一行代码:

1
2
#time.sleep(x)
await asyncio.sleep(x)

将time.sleep换为await asyncio.sleep,这样做的区别就是,await会主动告诉系统,我现在要执行一个IO操作,可以把空闲的cpu让出来,先做后面的事情,这样一来,当执行到sleep的时候,因为是IO操作,所以会自动切到后面的函数,可以近似的理解为两个函数在并行执行,当所有程序执行完用时4秒(以那个用时最长的为准)
因为协程是在线程中运行,所以切换函数效率极高,另外需要强调一点的就是,异步IO和并发是两个概念,千万不要混淆。

Pytorch终于支持Windows

发表于 2018-04-25 | 分类于 其他

喜大普奔!从今天起Pytorch正式支持Windows,当然需要Python版本3.5+

官网地址:http://pytorch.org/

pyroch

Pytorch入门之VAE

发表于 2018-04-11 | 分类于 算法

参考文献:http://www.cnblogs.com/king-lps/p/8477300.html

关于Auto-encoder

假设我们的输入是一张图片,Auto-encoder的工作其实是实现了 图片->向量->图片 这一过程。就是说给定一张图片编码后得到一个向量,然后将这一向量进行解码后就得到了原始的图片。这个解码后的图片和之前的原图一样吗?不完全一样。因为一般而言,如前所述是从低维隐层中恢复原图。但是Auto-encoder另我们现在能训练任意多的图片,如果我们把这些图片的编码向量存在来,那以后就能通过这些编码向量来重构我们的图像,称之为标准自编码器。可这还不够,如果现在我随机拿出一个很离谱的向量直接另其解码,那解码出来的东西十有八九是无意义的东西。
auto-encoder

变分自动编码器VAE(Variational Auto-encoder)

所以我们希望AE编码出的code符合一种分布(eg:高斯混合模型),那么我们就可以从这个高斯分布任意采样出一个code,给这个code解码那么就会生成一张原图类似的图。而这个强迫分布就是VAE与AE的不同之处了。VAE的编码器输出包括两部分:m和σ。其中e是正态分布, c为编码结果。m、e、σ、c的形状一样,都为(batch_size,latent_code_num) 。这个latent_code_num就相当于高斯混合分布的高斯数量。每个高斯都有自己的均值、方差。所以共有latent_code_num个均值、方差。


接下来是VAE的损失函数:由两部分的和组成(bce_loss、kld_loss)。bce_loss即为binary_cross_entropy(二分类交叉熵)损失,即用于衡量原图与生成图片的像素误差。kld_loss即为KL-divergence(KL散度),用来衡量潜在变量的分布和单位高斯分布的差异。
auto-encoder

阅读全文 »

手写Python反向传播

发表于 2018-03-30 | 分类于 算法

1.概要

本文来自博客 A Neural Network in 13 lines of Python
这篇博客只用了13行代码就实现了Python版本的反向传播,虽然有很多文章都对它进行了翻译,但在最关键反向传播部分,并没有给出详细公式,下面就对代码中的反向传播进行重点介绍。

2.全部反向传播代码

下面是13行完整代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import numpy as np
X = np.array([ [0,0,1],[0,1,1],[1,0,1],[1,1,1] ])
y = np.array([[0,1,1,0]]).T
lr,hidden_dim = (0.5,4)
w0 = 2*np.random.random((3,hidden_dim)) - 1
w1 = 2*np.random.random((hidden_dim,1)) - 1
for j in range(60000):
layer1 = 1/(1+np.exp(-(np.dot(X,w0))))
layer2 = 1/(1+np.exp(-(np.dot(layer1,w1))))
layer2_delta = (layer2 - y)*(layer2*(1-layer2))
layer1_delta = layer2_delta.dot(w1.T) * (layer1 * (1-layer1))
w1 -= (lr * layer1.T.dot(layer2_delta))
w0 -= (lr * X.T.dot(layer1_delta))
print(layer2)

3.关键行解释

变量 定义
X 输入数据矩阵,每行是一个训练样例
y 输出数据矩阵,每行是一个训练样例
layer1 神经网络的隐含层,输入来自X
layer2 神经网络的输出层,输入来自layer1
w1 输出层权重,连接layer1与layer2
w0 隐含层权重,连接X与layer1

line1-6: 前6行都是在定义初始化参数,学习率和权重矩阵等等,这些应该很好理解。
line8-9: 这两行是在进行前向传播,激活函数为sigmoid。
line10-13: 这四行是在进行反向传播,下面来说说代码为什么这么写

阅读全文 »

django.conf.settings的注意事项

发表于 2018-03-22 | 分类于 编程

因为在settings中加了自定义配置项,但是用django.conf.settings却无论如何都加载不到,还好在下面找到了答案。
https://stackoverflow.com/questions/8780756/django-difference-between-import-django-conf-settings-and-import-settings
总而言之,如果你想通过django.conf.settings加载你的自定义配置,请务必在settings.py中将你的配置全部大写!
毕竟在源码中这么写的

1
2
3
4
5
6
7
# 你的配置项
mod = importlib.import_module(self.SETTINGS_MODULE)

for setting in dir(mod):
# 判断是否都是大写
if setting.isupper():
setting_value = getattr(mod, setting)

小结Python中的元类Metaclass

发表于 2018-03-08 | 分类于 编程

关于metaclass,不用把它想的很复杂,只要和函数的装饰器做类比就好。通俗的说,装饰器是对函数参数的预处理,metaclass是对类属性的预处理。
下面拿一段代码来做解释:

1
2
3
4
5
6
7
8
9
10
11
12
13
class UpperAttrMetaClass(type): # to uppercase all attrs
def __new__(mcs, class_name, class_parents, class_attr):
attrs = {name if name.startswith('__') else name.upper(): value for name, value in class_attr.items()}
return super().__new__(mcs, class_name, class_parents, attrs)

class Base(metaclass=UpperAttrMetaClass):
bar = 1
def __init__(self, params):
super().__init__()
self.params = params
base = Base(1)
print(base.BAR)
print(base.bar)

这个UpperAttrMetaClass的功能就是把目标类中的所有类属性(不包含__开头的)全部转换为大写。

阅读全文 »

浅谈python2到python3的区别

发表于 2018-02-27 | 分类于 编程

随着numpy和django相继宣布不再支持python2.x,我想是时候进入python3.x的时代了。那么今天我们就来看看从python2到python3都有什么重大的改进。

1.通过 @ 实现矩阵乘法

1
2
3
4
5
6
7
import numpy as np
X = np.random.rand(2,2)
W = np.random.rand(2,2)
# python2
Y = W.dot(X)
# python3
Y = W @ X

2.类型提示 → 运行时的类型检查

python在给别人阅读时,有个最大的困扰,就是参数传递类型不明确,给读者造成很大困扰,在python3中加入了type hinting,具体操作如下

1
2
def test(a:int,b:str)->int:
pass

这种写法和scala的写法极其相似,也不知道是谁模仿的谁

1
2
def test(a:Int,b:String):Int={
}

可以看出,指定了a的类型为int,b的类型是str,返回类型为int
但是这种写法仅仅是增加可读性,优化IDE的提示效果,如果想强制限制类型,
需要安装一个模块enforce

阅读全文 »

解决django日志多进程写入混乱的问题

发表于 2018-02-11 | 分类于 编程

用过django自带日志配置的人都知道logging.handlers.RotatingFileHandler功能,但是最近在项目中发现,如果uwsgi配置django启动多进程的话,即便是开启了日志滚动功能,也不能保证所有日志都会写入当前最新的日志中,这究竟是为什么呢?
让我们看看下面的实验

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#coding=utf-8
import os
import time
import multiprocessing

f = open("./1.log","a")
def write():
while True:
f.write("Hello World\n",)
f.flush()
print("write line")
time.sleep(2)
if __name__=='__main__':
p = multiprocessing.Process(target=write)
p.start()

阅读全文 »
Kommmy

Kommmy

记录踩过的坑

9 日志
3 分类
3 标签
RSS
GitHub
© 2019 Kommmy
由 Hexo 强力驱动
|
总访问量