Sorry, your browser cannot access this site
This page requires browser support (enable) JavaScript
Learn more >

Gray-Ice

个人博客兼个人网站

生成验证码

注意,代码中的r是实例化了Redis对象,r.set()是向redis中存入键值对的方法。

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
import random
from PIL import Image
from PIL import ImageDraw
from django.http import HttpResponse
import io


class MyCode(View):

def get_random_color(self):
r = random.randrange(255)
g = random.randrange(255)
b = random.randrange(255)
return (r, g, b)

def get(self, request, index):
# 画布
img_size = (120, 50)
# 定义图片对象
image = Image.new('RGB', img_size, 'white')
# 定义画笔
draw = ImageDraw.Draw(image, 'RGB')
source = '01234567890abcdefghijklmn'
# 接收容器
code_str = ''
# 进入循环绘制
for i in range(4):
# 获取字母颜色
text_color = self.get_random_color()
# 获取随机下标
tmp_num = random.randrange(len(source))
# 随机字符串
random_str = source[tmp_num]
# 装入容器
code_str += random_str
# 绘制字符串
draw.text((10 + 30 * i, 20), random_str, text_color)
# 获取缓存区
buf = io.BytesIO()
# 将临时图片保存到缓冲
image.save(buf, 'png')

r.set('code', code_str)

return HttpResponse(buf.getvalue(), 'image/png')

这里get方法加了个index,是为了方便实现验证码的点击刷新。

前端展示验证码

下面是urls.py的代码:

1
path('code/<index>', MyCode.as_view(), name='code')

这里的index是为了从前端接收参数,接收了参数之后就会返回一个新的验证码(新的验证码与这里接收的参数无关,这里的参数只是起到了一个更改url的作用,下面vue里会讲到),虽然我觉得不应该这样写,但是这是我目前为止能找到的最好的、最简单的方法了,如果我能找到更完美的方法会更新。

(2020年5月25日14:27:48): 前几天突然发现,可以不用在urls.py后面指定接收的参数,直接写成:

1
path('code/', MyCode.as_view(), name='code')

即可。

下面是Vue的代码:

这是template里的代码

1
2
3
4
5
<tr v-if="for_fresh > 0">
<td>验证码: <input type="text" v-model="img_code"></td>
<td><img :src="img_src" alt="验证码加载失败" @click="refresh1()"></td>

</tr>

这是script标签里的代码

1
2
3
4
5
6
7
8
9
10
11
data(){
return {
img_src: 'http://127.0.0.1:8000/code/1'
},
methods:{
refresh1:function(){
var num=Math.ceil(Math.random()*10);//生成一个随机数(防止缓存)
this.img_src = "http://127.0.0.1:8000/code?num=" + num;
}
}
}

这里我定义了一个refresh1函数,它的作用就是生成一个参数随机的URL,然后导致img标签的src出现变化,img标签就会重新加载。那么也许你会想为什么不点击一下就向后台发送一条请求呢?这样也不用传参了,岂不是更快捷?

那么很遗憾的告诉你,img并不会随着后台图片发生变化而重新加载。要问我为什么。。。。这都是血的教训。所以目前来看还是建议通过改变img标签的src属性来实现验证码的加载。
不过这里的代码是有一点问题的: 那就是后台随机生成的验证码有可能会连着几次随机到同一条字符串,也就是说会生成两张一样的图片或者颜色不一样字符串却一样的图片,这里我还没有想解决这个问题,因为把获得两个同样的验证码看成是惊喜也是可以的吧?虽说会导致刷新验证码的原因一般就是用户看不清验证码。。。。

如果想要实现提交的信息不对就刷新验证码,那就在判断提交后的返回数据的时候加上this.refresh1()吧!

本篇完结(真是氵了不少内容呢)

(2020年7月25号2:34 pm更新)今天发现之前的验证码做的有点愚蠢,因为只对一个键进行操作,导致如果有两个或多个用户同时注册或者刷新验证码,那么必然只有一个用户可以输入正确的验证码,而且这还是在其他用户不点击验证码刷新的情况下。这件事必定会导致严重的后果。于是今天想了一个方法,虽然我个人感觉也是有点蠢,但是目前只能想到这么个点子了。那么下面来说我的这个新办法的逻辑。

这个逻辑其实很简单,就是用户请求注册页面的时候向后台发送一条消息,后台生成一条唯一标识返回给用户,用户存下唯一标识,在请求验证码的时候带上唯一标识,后台将唯一标识与验证码的正确值作为键值对存入redis并设置过期时间。当用户提交注册信息时,携带上唯一标识一并提交给后端。后端从redis中提取出键名为该唯一标识的键值对进行比较,如果错误或键过期就返回给用户对应的信息,如果正确就继续向下执行。

这个逻辑很简单,丝毫看不出优雅,这是让我感到无法接受的一点。但是秉承着”能用就行”的想法,我还是暂时使用了这个方法,当然,如果有邮箱注册或手机号注册的话,基本就用不着这样的验证码了,如果非要多加一条验证码,我认为滑动验证码是更好的选择。

那么下面还是写一下实现。
后台的代码就不放出来了。
下面是vue的代码:
html中:

1
<img :src="img_src" alt="验证码加载失败" width="30%" @click="refresh_code">

method方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// 刷新验证码
refresh_code(){
var num=Math.ceil(Math.random()*10);//生成一个随机数(防止缓存)
this.img_src = "http://127.0.0.1:8000/code?uuid=" + localStorage.getItem('rgt_uuid') + '&num=' + num;
},
// 获取uuid,用于图形验证码注册。该uuid唯一。
get_uuid(){
axios({
url: 'http://127.0.0.1:8000/register/',
method: 'get',
}).then(res=>{

if(res.data.code === 200){
localStorage.setItem('rgt_uuid', res.data.uuid);
this.img_src = 'http://127.0.0.1:8000/code?uuid=' + localStorage.getItem('rgt_uuid')
return 0;
}
layer.msg('获取信息失败了') // 这里我是使用的是layui,作用相当于alert
})
}

直接拼接图片链接的url,将uuid携带进url中,并且设置一个随机数(num)以实现验证码的刷新。

评论



愿火焰指引你