ajax的CSRF,memcached,验证码

1.使用Ajax发送csrf_token

  • 在使用ajax提交表单的时候,是不会主动将csrf_token提交的,django会认为是跨域请求而拒绝.所以需要手工获取csrf_token的值,并封装在ajax的beforesend,作为提交数据包的头部一块发送给服务器.
仅展示ajax部分代码
  • 前端点击按钮触发event事件,event.preventDefault()方法阻止了系统默认事件.
  • csrf_token是封装在发送数据包的头部,这部分在ajax是通过beforeSend实现,通过回调函数function(xhr,settings)传入对象xhr,以及对象的设置settings
  • 获取csrf_token有标签获取法$('input[name="csrfmiddlewaretoken"]').val(),直接取input标签中名称为csrfmiddlewaretoken输入框内容.
  • 也可以使用getCookie('csrftoken')方法,从传入的cookie中获取csrftoken的值.
  • 设置请求的头文件,使用xhr.setRequestHeader('X-CSRFToken',csrftoken)方法,前者是参数,后面是上面获取的csrf_token的值.
$('.submit-btn').click(function(event) {
                    event.preventDefault(); #阻止系统默认的事件方式
                    var username = $('.username-input').val(); #点击按钮后获取用户名,密码输入框里的值.
                    var password = $('.password-input').val();
                    $.ajax({
                        'url': '',  #请求的URL地址
                        'method': 'POST',  #post方式
                        'data': {                   #data部分的内容,字典格式
                            'username': username,
                            'password': password,
                        },
                        'beforeSend': function(xhr,settings) {   #设置要发送的头内容
                            //首先判断是不是post方法且没有跨域
                            if (!((/^(GET|HEAD|OPTIONS|TRACE)$/.test(settings.method))) && !this.crossDomain) {   #如果是post方法,且不是跨域
                                //1.获取csrf_token的值
                                //name="csrfmiddlewaretoken" 的input元素是渲染{%csrf_token%}后得到的
                                //var csrftoken = $('input[name="csrfmiddlewaretoken"]').val(); #通过input[name="csrfmiddlewaretoken"]获取
                                //优雅的获取token的方式如下:
                                var csrftoken = getCookie('csrftoken'); #从cookie中获取csrf_token值
                                //2.在header当中设置csrf_token的值
                                xhr.setRequestHeader('X-CSRFToken',csrftoken);  #设置xhr对象请求的headers中的CSRFToken参数为csrftoken值
                            }
                        },
                        'success': function(data) {  #如果请求成功
                            console.log('success')
                            if(data['code'] == 200){   #状态码200
                                window.location = '/'; #返回到首页
                            }else{
                                <!--console.log(data.box)-->
                                console.log(data['message']); #如果状态不为200,获取信息和状态码
                                console.log(data['code']);
​
                                var message = data['message'];
                                var messageJson = JSON.parse(message);
                                var errorlist = messageJson['password'];
                                var codename = errorlist[0];
                                var error = codename['message'];
                                var code= codename['code'];
                                console.log(codename['code']);
                                $('.error-info').html(error);
                            }
                        },
                        'error':function() {
                            console.log('error');
                        },
                        'complete': function() {
                            console.log('complete');
                        }
                    });
                });
            });
csrf_token在源代码中的显示

在请求页面过程中,response的headers里面也包含了csrf_token

2.memcached

  • memcache是一个缓存数据库,通常情况下,数据库读取次数远大于写入次数,频繁的数据库硬盘io读取操作会造成性能下降.缓存数据库是在内存上工作,用于加速数据的读写.
memcached安装步骤:

2.1 memcached的安装
#1.先安装依赖包 libevent-2.0-5
ddc@SERVER:~$ sudo apt-get install libevent-2.0-5
Reading package lists... Done
Building dependency tree       
Reading state information... Done
libevent-2.0-5 is already the newest version (2.0.21-stable-2ubuntu0.16.04.1).
The following packages were automatically installed and are no longer required:
  libllvm3.8 linux-headers-4.4.0-62 linux-headers-4.4.0-62-generic linux-headers-4.4.0-75
  linux-headers-4.4.0-75-generic linux-headers-4.4.0-78 linux-headers-4.4.0-78-generic
  linux-headers-4.4.0-79 linux-headers-4.4.0-79-generic linux-headers-4.4.0-81
  linux-headers-4.4.0-81-generic linux-headers-4.4.0-83 linux-headers-4.4.0-83-generic
  linux-headers-4.4.0-87 linux-headers-4.4.0-87-generic linux-headers-4.4.0-89
  linux-headers-4.4.0-89-generic linux-headers-4.4.0-91 linux-headers-4.4.0-91-generic
  linux-headers-4.4.0-92 linux-headers-4.4.0-92-generic linux-image-4.4.0-62-generic
  linux-image-4.4.0-75-generic linux-image-4.4.0-78-generic linux-image-4.4.0-79-generic
  linux-image-4.4.0-81-generic linux-image-4.4.0-83-generic linux-image-4.4.0-87-generic
  linux-image-4.4.0-89-generic linux-image-4.4.0-91-generic linux-image-4.4.0-92-generic
  linux-image-extra-4.4.0-62-generic linux-image-extra-4.4.0-75-generic linux-image-extra-4.4.0-78-generic
  linux-image-extra-4.4.0-79-generic linux-image-extra-4.4.0-81-generic linux-image-extra-4.4.0-83-generic
  linux-image-extra-4.4.0-87-generic linux-image-extra-4.4.0-89-generic linux-image-extra-4.4.0-91-generic
  linux-image-extra-4.4.0-92-generic
Use 'sudo apt autoremove' to remove them. #这里因为已经安装过了,所以显示可以使用autoremove移除
0 upgraded, 0 newly installed, 0 to remove and 29 not upgraded.
#2.安装memcached
ddc@SERVER:~$ sudo apt-get install memcached  
Reading package lists... Done
Building dependency tree       
Reading state information... Done
memcached is already the newest version (1.4.25-2ubuntu1.2).
The following packages were automatically installed and are no longer required:
  libllvm3.8 linux-headers-4.4.0-62 linux-headers-4.4.0-62-generic linux-headers-4.4.0-75
  linux-headers-4.4.0-75-generic linux-headers-4.4.0-78 linux-headers-4.4.0-78-generic
  linux-headers-4.4.0-79 linux-headers-4.4.0-79-generic linux-headers-4.4.0-81
  linux-headers-4.4.0-81-generic linux-headers-4.4.0-83 linux-headers-4.4.0-83-generic
  linux-headers-4.4.0-87 linux-headers-4.4.0-87-generic linux-headers-4.4.0-89
  linux-headers-4.4.0-89-generic linux-headers-4.4.0-91 linux-headers-4.4.0-91-generic
  linux-headers-4.4.0-92 linux-headers-4.4.0-92-generic linux-image-4.4.0-62-generic
  linux-image-4.4.0-75-generic linux-image-4.4.0-78-generic linux-image-4.4.0-79-generic
  linux-image-4.4.0-81-generic linux-image-4.4.0-83-generic linux-image-4.4.0-87-generic
  linux-image-4.4.0-89-generic linux-image-4.4.0-91-generic linux-image-4.4.0-92-generic
  linux-image-extra-4.4.0-62-generic linux-image-extra-4.4.0-75-generic linux-image-extra-4.4.0-78-generic
  linux-image-extra-4.4.0-79-generic linux-image-extra-4.4.0-81-generic linux-image-extra-4.4.0-83-generic
  linux-image-extra-4.4.0-87-generic linux-image-extra-4.4.0-89-generic linux-image-extra-4.4.0-91-generic
  linux-image-extra-4.4.0-92-generic
Use 'sudo apt autoremove' to remove them.
0 upgraded, 0 newly installed, 0 to remove and 29 not upgraded.
#安装完成后使用ls -al /usr/bin/mem*检查是否已经安装上ddc@SERVER:~$ ls -al /usr/bin/mem* 
-rwxr-xr-x 1 root root 140708 Nov  2  2016 /usr/bin/memcached
2.2 memcached的启动
#使用命令memcached启动缓存数据库
ddc@SERVER:~$ memcached -d -m 1024 -u root -l 127.0.0.1 -p 12000 -c 256 -P /memcached/memcached.pid
ddc@SERVER:~$ ps aux | grep memcached
memcache  1146  0.0  0.1  47712  2020 ?        Ssl  Sep24   0:05 /usr/bin/memcached -m 64 -p 11211 -u memcache -l 127.0.0.1
ddc       5331  0.0  0.1  44232  1592 ?        Ssl  00:52   0:00 memcached -d -m 1024 -u root -l 127.0.0.1 -p 12000 -c 256 -p /memcached/memcached.pid
ddc       5338  0.0  0.0   5104   844 pts/0    S+   00:53   0:00 grep --color=auto memcached
#检查进程是否启动成功.
ddc@SERVER:~$ ps aux | grep memcached
memcache  1146  0.0  0.1  47712  2020 ?        Ssl  Sep24   0:05 /usr/bin/memcached -m 64 -p 11211 -u memcache -l 127.0.0.1
ddc       5331  0.0  0.1  44232  1592 ?        Ssl  00:52   0:00 memcached -d -m 1024 -u root -l 127.0.0.1 -p 12000 -c 256 -p /memcached/memcached.pid
ddc       5340  0.0  0.0   5104   840 pts/0    S+   00:53   0:00 grep --color=auto memcached
#检查是否监听端口
ddc@SERVER:~$ netstat -tnlp
(Not all processes could be identified, non-owned process info
 will not be shown, you would have to be root to see it all.)
Active Internet connections (only servers)
Proto Recv-Q Send-Q Local Address           Foreign Address         State       PID/Program name    tcp        0      0 0.0.0.0:12530           0.0.0.0:*               LISTEN      5466/memcached      tcp        0      0 127.0.0.1:12000         0.0.0.0:*               LISTEN      5353/memcached      tcp6       0      0 :::12530                :::*                    LISTEN      5466/memcached    
#服务成功启动.
安装python-memcached
ddc@SERVER:~$ sudo pip install python-memcached
The directory '/home/ddc/.cache/pip/http' or its parent directory is not owned by the current user and the cache has been disabled. Please check the permissions and owner of that directory. If executing pip with sudo, you may want sudo's -H flag.
The directory '/home/ddc/.cache/pip' or its parent directory is not owned by the current user and caching wheels has been disabled. check the permissions and owner of that directory. If executing pip with sudo, you may want sudo's -H flag.
Collecting python-memcached
  Downloading python-memcached-1.58.tar.gz
Requirement already satisfied: six>=1.4.0 in /usr/lib/python3/dist-packages (from python-memcached)
Installing collected packages: python-memcached
  Running setup.py install for python-memcached ... done
Successfully installed python-memcached-1.58
2.3 python-memcached用法
#进入python
ddc@SERVER:~$ python 
Python 2.7.12 (default, Nov 19 2016, 06:48:10) 
[GCC 5.4.0 20160609] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> import os #导入os模块
>>> os.system('/usr/bin/memcached -p 12530 -m 64 -c 2048 -t 8 -d -u nobody') #启动memcached
0
#-u 指定用户,然后缓存的空间为64M(-m), 监听(-l)服务器10.1.41.113的11212号端口(-p)
>>> import memcache #导入memcached
>>> mc=memcache.Client(['127.0.0.1:12530'],debug=0) #连接memcached客户端,不使用调试模式
>>> mc.set('长沙南','CSZ') #将key,value存入,成功返回True
True
>>> mc.get('长沙南') #获取key为长沙南的value
'CSZ'
#2、指定端口
mc=memcache.Client(['127.0.0.1:11211'],debug=0) #注意:-p端口号要和Client中端口一致
主要用法
set(key,val,time=0,min_compress_len=0)
#无条件键值对的设置,其中的time用于设置超时,单位是秒,而min_compress_len则用于设置zlib压缩(注:zlib是提供数据压缩用的函式库)
set_multi(mapping,time=0,key_prefix='',min_compress_len=0)
add(key,val,time=0,min_compress_len=0)
#添加一个键值对,内部调用_set()方法
replace(key,val,time=0,min_compress_len=0)
#替换value,内部调用_set()方法
get(key)
#根据key去获取value,出错返回None
get_multi(keys,key_prefix='')
#获取多个key的值,返回的是字典。keys为key的列表
delete(key,time=0)
#删除某个key。time的单位为秒,用于确保在特定时间内的set和update操作会失败。如果返回非0则代表成功
incr(key,delta=1)自增变量加上delta,默认加1
decr(key,delta=1)自减变量减去delta,默认减1

3.django中配置memcached

在django中要使用memcached非常简单,只需要在settings里面增加一段如下代码即可.
CACHES={
    'default':{
        'BACKEND':'django.core.cache.backends.memcached.MemcachedCache',
        'LOCATION':[
            '127.0.0.1:11211',
        ],
    }
}

4.验证码的生成

通过对登陆界面的分析,可以看到,验证码其实是img标签请求/comm/captcha/得到的一张显示的图片.
  • 验证码的实现是使用了python的内置库,存放在utils.captcha目录下captcha.py

目录结构

其中的ttf就是字体文件库,也可以在c:/windows/fonts/目录拷贝进来.
  • @classmethod使用了类方法,将一些变量进行了公用
    • 验证码生成主要看gene_code方法,首先获取cls.size的宽高度
    • 使用Image.new('RGBA',(width,height),cls.bgcolor) 生成图片对象image
    • 使用ImageFont.truetype(cls.font_path,cls.fontsize)获取生成验证码的字体
    • 使用ImageDraw.Draw(image)创建画笔对象draw
    • 使用cls.__gene_text()生成字体的字符串text
    • 从text中font_width, font_height = font.getsize(text)获得字体的实际长宽高.
    • 使用draw.text(((width - font_width) / 2, (height - font_height) / 8),text,font= font,fill=cls.fontcolor)把字符填充到图片中.这里使用画布的宽度–字体的宽带,得到的距离除以2,是为了让字体居中好看,不知义溢出.
    • 也可以使用cls.__gene_line(draw,width,height)cls.__gene_points(draw,10,width,height)这两种私有方法产生干扰线和干扰点.
  • 随后将生成的验证码,转换为小写存放缓存数据库,存放时间120秒.cache.set(text.lower(),text.lower(),120)
#captcha.py
# -*- coding: utf-8 -*-
import random
import string
import sys
import math
# pip install Pillow
# Image:是一个画板(context),ImageDraw:是一个画笔, ImageFont:画笔的字体
from PIL import Image,ImageDraw,ImageFont,ImageFilter
from django.conf import settings
from django.core.cache import cache# Captcha验证码class Captcha(object):
    # 把一些常量抽取成类属性
    #字体的位置
#font_path = 'verdana.ttf'
    font_path = 'utils/captcha/verdana.ttf'
    #生成几位数的验证码
    number = 4
    #生成验证码图片的宽度和高度
    size = (100,30)
    #背景颜色,默认为白色 RGB(Re,Green,Blue)
    bgcolor = (255,255,255)
    #字体颜色,默认为蓝色
    fontcolor = (random.randint(0,100),random.randint(0,100),random.randint(0,100))
    # 验证码字体大小
    fontsize = 25
    #干扰线颜色。默认为红色
    linecolor = (random.randint(0,255),random.randint(0,255),random.randint(0,255))
    # 是否要加入干扰线
    draw_line = True
    # 是否绘制干扰点
    draw_point = True
    # 加入干扰线的条数
    line_number = 2
​
​
    #用来随机生成一个字符串(包括英文和数字)
    # 定义成类方法,然后是私有的,对象在外面不能直接调用
    @classmethod
    def __gene_text(cls):
        source = list(string.letters)
        for index in range(0,10):
            source.append(str(index))
        return ''.join(random.sample(source,cls.number))#number是生成验证码的位数
​
    #用来绘制干扰线
    @classmethod
    def __gene_line(cls,draw,width,height):
        begin = (random.randint(0, width), random.randint(0, height))
        end = (random.randint(0, width), random.randint(0, height))
        draw.line([begin, end], fill = cls.linecolor)
​
    # 用来绘制干扰点
    @classmethod
    def __gene_points(cls,draw,point_chance,width,height):
      chance = min(100, max(0, int(point_chance))) # 大小限制在[0, 100]
      for w in xrange(width):
        for h in xrange(height):
          tmp = random.randint(0, 100)
          if tmp > 100 - chance:
            draw.point((w, h), fill=(0, 0, 0))
​
    #生成验证码
    @classmethod
    def gene_code(cls): #对象的函数方法用self,类的方法用cls
        width,height = cls.size #宽和高
        image = Image.new('RGBA',(width,height),cls.bgcolor) #创建图片
        font = ImageFont.truetype(cls.font_path,cls.fontsize) #验证码的字体
        draw = ImageDraw.Draw(image)  #创建画笔
        text = cls.__gene_text() #生成字符串
        print 'text=',text
        font_width, font_height = font.getsize(text)
        draw.text(((width - font_width) / 2, (height - font_height) / 8),text,font= font,fill=cls.fontcolor) #填充字符串
        # 如果需要绘制干扰线
        if cls.draw_line:
            # 遍历line_number次,就是画line_number根线条
            for x in xrange(0,cls.line_number):
                cls.__gene_line(draw,width,height)
        # 如果需要绘制噪点
        if cls.draw_point:
            cls.__gene_points(draw,10,width,height)
        # 保存到缓存中,过期时间为2分钟
        print 'text.lower()=', text.lower()
        cache.set(text.lower(),text.lower(),120)
        #保存图片到缓存数据库,并设置有效期,有效期单位是秒
        #set(key,value,time)
        #cache.set(text,text,120)
#cache.set(text,text,120)
        return (text,image)
​
    @classmethod
    def check_captcha(cls,captcha):
        captcha_cache = cache.get(captcha)
        print 'captcha_cache=',captcha_cache
        print 'captcha=', captcha
        if captcha_cache and captcha_cache == captcha:
            # 删除缓存
            cache.delete(captcha)
            print u'captcha与缓存一致'
            return True
        else:
            return False
当用户进入登陆界面后,img标签会自动get的 /comm/captcha/路由
<span class="input-group-addon">
                    <img src="/comm/captcha/" alt="点击刷新验证码" class="captcha-img">
</span>
映射到路由common_captcha
urlpatterns = [
    url(r'^captcha/',views.captcha,name='common_captcha'),
]
对应的视图函数captcha
  • 首先生成text和image,调用Captcaha的模块
  • 通过StringIO()方法生成管道out
  • 把图像信息save到管道里
  • 使用out.seek(0)
from cStringIO import StringIO
def captcha(request):
    #生成验证码并发送到浏览器
    #1,生成验证码文本和图片
    text,image = Captcha.gene_code()
    # image.save('text.png','png') # I/0#2,通过StringIO(类似ramdisk)发送到前端
    #需要通过StringIO这个类来把图片当成流的形式返回给客户端
    out = StringIO() #获取"管道"
    image.save(out,'png') #把图片保存到管道中
    out.seek(0) #移动文件指针到\第0个位置
    response = HttpResponse(content_type='image/png')
    response.write(out.read())
    # 把验证码数据写入到缓存中,过期时间是2分钟
    # key = text.lower()
    # value = key
    # cache.set(key,value,120)
    return response
提交回来的检查逻辑
  • 从cachea总获取cachede带captcha部分内容了
  • 比较缓存中的数值是否相等
  • 如果相等的话,皆可以删除缓存中的数据并返回true给前台.
    @classmethod
    def check_captcha(cls,captcha):
        captcha_cache = cache.get(captcha)
        print 'captcha_cache=',captcha_cache
        print 'captcha=', captcha
        if captcha_cache and captcha_cache == captcha:
            # 删除缓存
            cache.delete(captcha)
            print u'captcha与缓存一致'
            return True
        else:
            return False

总结

jQuery发送csrf_token是进行表单跨域安全请求的重要保障环节.而memcached缓存的出现,减轻了数据库频繁的io读写.Python提供了一种生成验证码的方式,建议使用.
赞(0) 打赏
未经允许不得转载:http://www.yueguangzu.net逗逗网 » ajax的CSRF,memcached,验证码
分享到: 更多 (0)

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

微信扫一扫打赏