# 驗證碼
## 驗證碼
* 在用戶注冊、登錄頁面,為了防止暴力請求,可以加入驗證碼功能,如果驗證碼錯誤,則不需要繼續處理,可以減輕一些服務器的壓力
* 使用驗證碼也是一種有效的防止crsf的方法
* 驗證碼效果如下圖:

### 驗證碼視圖
* 定義vrrifycode函數視圖
* 使用pillow,pip install pillow
* Image標識畫布對象
* ImageDraw表示畫筆對象
* ImageFont表示字體對象
* 代碼如下:
```text
from django.http import HttpResponse
def verifycode(request):
#引入繪圖模塊
from PIL import Image, ImageDraw, ImageFont
#引入隨機函數模塊
import random
#定義變量,用于畫面的背景色、寬、高
bgcolor = (random.randrange(20, 100), random.randrange(
20, 100), 255)
width = 100
height = 25
#創建畫面對象
im = Image.new('RGB', (width, height), bgcolor)
#創建畫筆對象
draw = ImageDraw.Draw(im)
#調用畫筆的point()函數繪制噪點
for i in range(0, 100):
xy = (random.randrange(0, width), random.randrange(0, height))
fill = (random.randrange(0, 255), 255, random.randrange(0, 255))
draw.point(xy, fill=fill)
#定義驗證碼的備選值
str1 = 'ABCD123EFGHIJK456LMNOPQRS789TUVWXYZ0'
#隨機選取4個值作為驗證碼
rand_str = ''
for i in range(0, 4):
rand_str += str1[random.randrange(0, len(str1))]
#構造字體對象
font = ImageFont.truetype('arial.ttf', 36)
#構造字體顏色
fontcolor = (255, random.randrange(0, 255), random.randrange(0, 255))
#繪制4個字
draw.text((5, 2), rand_str[0], font=font, fill=fontcolor)
draw.text((25, 2), rand_str[1], font=font, fill=fontcolor)
draw.text((50, 2), rand_str[2], font=font, fill=fontcolor)
draw.text((75, 2), rand_str[3], font=font, fill=fontcolor)
#釋放畫筆
del draw
#存入session,用于做進一步驗證
request.session['verifycode'] = rand_str
#內存文件操作
from io import BytesIO
buf = BytesIO()
#將圖片保存在內存中,文件類型為png
im.save(buf, 'png')
#將內存中的圖片數據返回給客戶端,MIME類型為圖片png
return HttpResponse(buf.getvalue(), 'image/png')--
```
## 配置url
* 在urls.py中配置路由,請求驗證碼視圖的url
```text
from django.conf.urls import url,include
from . import views
app_name = 'myapp'
urlpatterns = [
url('^image/$',views.verifycode),
]
```
## 顯示驗證碼
* 在模板中使用img標簽,src指向驗證碼視圖
```text
<img id='verifycode' src="/verifycode/" alt="CheckCode"/>
```
* 啟動服務器,查看顯示成功
* 擴展:點擊"看不清,換一個"時,可以換一個新的驗證碼
```text
<script type="text/javascript" src="/static/jquery-1.12.4.min.js"></script>
<script type="text/javascript">
$(function(){
$('#verifycodeChange').css('cursor','pointer').click(function() {
$('#verifycode').attr('src',$('#verifycode').attr('src')+1)
});
});
</script>
<img id='verifycode' src="/verifycode/?1" alt="CheckCode"/>
<span id='verifycodeChange'>看不清,換一個</span>
```
* 為了能夠實現提交功能,需要增加form和input標簽
```text
<form method='post' action='/verifycodeValid/'>
<input type="text" name="vc">
<img id='verifycode' src="/verifycode/?1" alt="CheckCode"/>
<span id='verifycodeChange'>看不清,換一個</span>
<br>
<input type="submit" value="提交">
</form>
```
## 驗證
* 接收請求的信息,與session中的內容對比
```text
def verifycodeValid(request):
name = request.POST['name']
if name.upper() == request.session['verifycode']:
return HttpResponse('ok')
else:
return HttpResponse('no')
```
## 完整代碼
login.html模板
```text
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<form method="post" action="{% url 'myapp:verifycodeValid' %}">
{% csrf_token %}
<input type="text" name="name"><br />
<img src="verifycode" srcset="{% url 'myapp:verifycode' %}?1" alt="CheckCode"/>
<span id="verifycodeChange">看不清,換一個</span><br />
<input type="submit" value="submit"/>
</form>
<script type="text/javascript">
$(function () {
$('#verifycodeChange').css('cursor','pointer').click(function () {
$('#verifycode').attr('src',$('#verifycode').attr('src')+1)
});
});
</script>
</body>
</html>
```
views.py視圖
```text
from django.http import HttpResponse
def verifycode(request):
# 引入繪圖模塊
from PIL import Image,ImageDraw,ImageFont
# 引入隨機函數模塊
import random
# 定義變量,用于畫面的背景色、寬、高
# random.randrange(start,stop,step),隨機選取一個
bgcolor = (random.randrange(20,100),random.randrange(20,100),255)
width = 200
height = 50
# 創建畫面對象
im = Image.new('RGB',(width,height),bgcolor)
# 創建畫筆對象
draw = ImageDraw.Draw(im)
# 調用畫筆的point()函數繪制噪點
for i in range(0,100):
xy = (random.randrange(0,width),random.randrange(0,height))
fill = (random.randrange(0,255),255,random.randrange(0,255))
draw.point(xy,fill=fill)
# 定義驗證碼的備選值
str1 = 'ABCD123EFGHIJK456LMNOPQRS789TUVWXYZ0'
# 隨機選取4個值作為驗證碼
rand_str = ""
for i in range(0,4):
rand_str += str1[random.randrange(0, len(str1))]
# 構造字體對象
font = ImageFont.truetype('arial.ttf', 36)
# 構造字體顏色
fontcolor = (255,random.randrange(0,255),random.randrange(0,255))
# 繪制4個字
draw.text((5,2),rand_str[0],font=font,fill=fontcolor)
draw.text((25, 2), rand_str[1], font=font, fill=fontcolor)
draw.text((50, 2), rand_str[2], font=font, fill=fontcolor)
draw.text((75, 2), rand_str[3], font=font, fill=fontcolor)
#釋放畫筆
del draw
# 存入session,用于做進一步驗證
request.session['verifycode'] = rand_str
# 內存文件操作
from io import BytesIO
buf = BytesIO()
# 將圖片保存在內存中,文件類型為png
im.save(buf,'png')
# 將內存中的圖片數據返回給客戶端,MIME類型為圖片png
return HttpResponse(buf.getvalue(),'image/png')
def verifycodeValid(request):
name = request.POST['name']
if name.upper() == request.session['verifycode']:
return HttpResponse('ok')
else:
return HttpResponse('no')
def login(request):
return render(request,'myapp/login.html')
```
urls.py配置路由
```text
url(r'^login/$',views.login,name="login"),
url('^image/$',views.verifycode,name="verifycode"),
url(r'^verifycodeValid/$',views.verifycodeValid,name="verifycodeValid"),
```