目录
前言
在验证码原理与Django实现--简陋滑块验证码中所实现的滑块验证码是其最简单的一个实现,但是与大多数网页中的滑块验证码尚有出入。本文展示与网页接近的滑块验证码实现
滑块验证码的组成无非是两部分:带空缺的图片和滑块(即与空缺相匹配的图片)。要实现滑块验证码就是要解决这两部分
后端
随机选取图片
首先从文件夹中随机取出一张图片,将图片的地址存储到session中(其他函数需要对该图片进行处理),然后通过随机数确定所截取图片的坐标(纵坐标y需要传给前端以保证截取部分与空缺部分平行)
def detail_puzzle(request):
# 判断部分
if request.method == 'POST':
pass
# 从文件夹中随机选取图片
all_images = list(os.listdir('././static/imgs/slider'))
image_url = random.choice(all_images)
# 拼接完整地址
image_url = os.path.join('././static/imgs/slider', image_url)
# 将图片地址存储在session中
request.session['image_url'] = image_url
# 随机截取位置的x,y值(左上)
# 由于需要y对滑块位置进行确定,所有在此处将y赋值
x = random.randint(20, 390)
y = random.randint(15, 230)
request.session.update({'x': x, 'y': y})
return render(request, 'detail_slider.html', locals())
截取图片
通过session取出xy值,截取图片的一部分,并对图片该部分进行暗化处理。将所截取的图片也返回前端(使用的方法没有改变原图大小,所以部分代码重复出现)
def complete_image(request):
x = request.session.get('x')
y = request.session.get('y')
with Image.open(request.session.get('image_url')) as img_obj:
# 改变图片大小
# resize的返回值才是改变后的图片
img_obj = img_obj.resize((500, 300))
area = (x, y, x + 45, y + 45)
# 截取范围
darken_region = img_obj.crop(area)
# 此处虽然将图片改为了规定大小,但是截取
# 调整区域的亮度
enhancer = ImageEnhance.Brightness(darken_region)
darkened_region = enhancer.enhance(0.45)
# 将变暗的区域黏贴回去
img_obj.paste(darkened_region, area)
# 主要用于展示原图
io_obj = BytesIO()
img_obj.save(io_obj, 'png')
return HttpResponse(io_obj.getvalue())
def part_image(request):
# 拿到x,y值
x = request.session.get('x')
y = request.session.get('y')
# 划定范围
crop_area = (x, y, x + 45, y + 45)
# 获取图片地址
image_path = request.session.get('image_url')
# 打开图片
with Image.open(image_path) as img_obj:
# 改变图片大小
img_obj = img_obj.resize((500, 300))
# 截取指定区域
cropped_img = img_obj.crop(crop_area)
io_obj = BytesIO()
cropped_img.save(io_obj, 'png')
return HttpResponse(io_obj.getvalue())
判断部分
设置合理范围,对post请求返回的数据进行验证
def detail_puzzle(request):
# 只有完成滑动后才会进行获取并判断
if request.method == 'POST':
x = int(request.session.get('x')) + 150
final_x = int(request.POST.get('final_x'))
# 平行滑动,只判断x轴,设置合理范围
if final_x in range(x - 3, x + 4):
return JsonResponse({'info': 1})
else:
return JsonResponse({'info': 0})
# 随机选取图片部分
pass
前端
展示滑块与图片,并监听滑动
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>滑块验证</title>
{% load static %}
<link rel="stylesheet" href="{% static 'bootstrap-3.4.1-dist/css/bootstrap.min.css' %}">
<script src="{% static 'js/jquery-3.6.0.min.js' %}"></script>
<script src="{% static 'bootstrap-3.4.1-dist/js/bootstrap.min.js' %}"></script>
<style>
#container {
height: 300px;
width: 800px;
margin: auto;
position: relative;
}
.movable_image {
position: absolute;
transition: transform 0.1s ease;
}
</style>
</head>
<body>
<h1 style="text-align: center">请滑动滑块使图片完整</h1>
<div style="text-align: center;height: 300px;">
<div class="container">
<div class="row">
<div class="col-md-12">
<div id="container">
<span style="display: inline-block;margin-right: 0">
<img src="/picture/complete_image/">
</span>
<span style="top: {{ y }}px; left: 0;position: absolute">
<img src="/picture/part_image/" class="movable_image">
</span>
</div>
</div>
</div>
<div class="row" style="width: 300px; margin: auto">
<div class="col-md-12" method="post">
<p>使用滑动条控制图片的横移:</p>
<input type="range" min="0" max="600" value="0" class="custom-range" id="slider">
<button type="button" id="id_button" class="btn btn-success">刷新</button>
</div>
</div>
</div>
</div>
<script>
$('#id_button').click(function () {
location.reload();
});
$(document).ready(function() {
$('#slider').on('input', function() {
let value = $(this).val();
$('.movable_image').css('transform', 'translateX(' + value + 'px)');
});
});
$('#slider').one('mouseup', function() {
let value = $(this).val();
$.ajax({
type: 'POST',
data: {
'final_x': value,
},
success: function(args) {
if (args.info === 1) {
alert('成功');
} else{
alert('失败');
}
}
});
// 禁用滑动条
$('#slider').prop('disabled', true);
});
</script>
</body>
</html>
效果展示
误区总结
错误:前端展示的图片与预期不同
很明显,图片超出了预期
并且位置判断出现问题
解决尝试1:在前端限定图片大小
在img中加上
style="width: 500px;height: 300px"
于是更加离谱
解决尝试2:后端resize出现问题
可以观察到最初只有图片大小出现问题,而位置判断很可能是前者附带的。经过分析,发现最初resize的使用为:
with Image.open(image_path) as img_obj:
# 改变图片大小
img_obj.resize((500, 300))
但是resize函数的返回值才是改变后的图片对象
大二在读,不足请指正