滑块验证码实现,自己也可以手搓,灵活多变

众所周知,在网络爬虫横行的今天,网站为了保护自己的权益,不得不增加保护网站数据得安全措施。

源码:Q_Q 1690361973

好了,进入主题QAQ

一.首先就是如何生成缺口图跟带有缺口的背景图

     逻辑:利用PIL库将一张图片设置成自己想要的大小,然后根据random随机在图片上裁剪出缺口,然后保存缺口图片跟被裁剪过的带缺口的背景图。同时返回缺口的坐标,方便后面跟前端返回的坐标进行比较。

from PIL import Image, ImageDraw, ImageFilter
import random

def create_slider_captcha(image_path, output_dir, block_size=40, target_size=(260, 130)):
    # 打开图像并转换为 RGBA 格式
    image = Image.open(image_path).convert("RGBA")
    # 调整图像大小到目标尺寸
    image = image.resize(target_size, Image.Resampling.LANCZOS)
    width, height = target_size

    # 随机选择缺口的位置
    gap_left = random.randint(block_size, width - block_size * 2)
    gap_top = random.randint(0, height - block_size)

    # 创建图像的副本以绘制缺口
    background_with_gap = image.copy()
    draw = ImageDraw.Draw(background_with_gap)
    # 绘制一个透明的矩形以创建缺口
    draw.rectangle((gap_left, gap_top, gap_left + block_size, gap_top + block_size), fill=(255, 255, 255, 0))

    # 从原始图像中裁剪出滑块
    slider_block = image.crop((gap_left, gap_top, gap_left + block_size, gap_top + block_size))

    # 创建一个新的黑白遮罩图像
    mask = Image.new("L", (block_size, block_size), 0)
    draw_mask = ImageDraw.Draw(mask)
    # 在遮罩图像上绘制白色矩形
    draw_mask.rectangle((0, 0, block_size, block_size), fill=255)
    # 对遮罩图像应用高斯模糊
    mask = mask.filter(ImageFilter.GaussianBlur(3))
    
    # 将遮罩应用于滑块图像
    slider_block.putalpha(mask)

    # 保存带缺口的背景图像
    background_with_gap.save(f"{output_dir}/background_with_gap.png")
    # 保存滑块图像
    slider_block.save(f"{output_dir}/slider_block.png")

    # 返回缺口的坐标
    return ('gap_left:', gap_left, 'gap_top:', gap_top)

# 示例调用
gap_coordinates = create_slider_captcha("1.jpg", r"F:\Python_files\webhtml\output\/")
print(f"缺口坐标: {gap_coordinates}")

二.将图片加载到前端,这里的逻辑应该是将图片跟x坐标从服务器加载到前端的,但是重点是实现滑块,这里就改成本地加载了

<!DOCTYPE html>
<html lang="zh-CN">

<head>
    <meta charset="UTF-8">
    <title>滑块验证码</title>
    <style>
        /*滑块验证的css样式*/
        .captcha-container {
            position: relative;
            width: 260px;
            height: 130px;
            border: 1px solid #ccc;
            margin-bottom: 1px;
        }

        .captcha-image {
            width: 100%;
            height: 130px;
        }

        .slider-block {
            position: absolute;
            top: 0;
            width: 40px;
            height: 40px;
            z-index: 2;
        }

        .slider-container {
            width: 260px;
            height: 20px;
            background-color: #eee;
            border-top: 1px solid #ccc;
            display: flex;
            align-items: center;
            position: relative;
        }

        .slider-container::before {
            content: '向右滑动完成验证';
            position: absolute;
            left: 0;
            right: 0;
            text-align: center;
            font-size: 14px;
            color: #0c0c0c;
        }

        .slider {
            width: 40px;
            height: 100%;
            background-color: #0dddec;
            cursor: pointer;
            position: absolute;
            z-index: 3;
            display: flex;
            align-items: center;
            justify-content: center;
            font-size: 18px;
            color: #000;
        }

        .slider::before {
            content: '>';
        }
    </style>
</head>

<body>
    <div class="captcha-container">
        <img src="output/background_with_gap.png" class="captcha-image" id="captcha-image">
        <img src="output/slider_block.png" class="slider-block" id="slider-block" style="left: 0;">
    </div>
    <div class="slider-container">
        <div class="slider" id="slider" style="left: 0;"></div>
    </div>

    <script>
        const slider = document.getElementById('slider');
        const sliderBlock = document.getElementById('slider-block');
        let isDragging = false;
        let startX = 0;

        const gapLeft = 176; // 将此值替换为你的缺口的实际横坐标
        const gapTop = 64; // 将此值替换为你的缺口的实际纵坐标
        sliderBlock.style.top = gapTop + 'px';

        slider.addEventListener('mousedown', function (e) {
            isDragging = true;
            startX = e.clientX - parseInt(slider.style.left, 10); // 记录鼠标按下时的初始位置
        });

        document.addEventListener('mouseup', function () {
            if (isDragging) {
                isDragging = false;
                const currentLeft = parseInt(slider.style.left, 10);
                if (Math.abs(currentLeft - gapLeft) < 5) {
                    alert('验证通过');
                } else {
                    alert('验证失败');
                    slider.style.left = '0px';
                    sliderBlock.style.left = '0px';
                }
            }
        });

        document.addEventListener('mousemove', function (e) {
            if (isDragging) {
                const moveX = e.clientX - startX;
                if (moveX >= 0 && moveX <= (260 - 40)) {
                    slider.style.left = moveX + 'px';
                    sliderBlock.style.left = moveX + 'px';
                }
            }
        });
    </script>
</body>

</html>

本地实现,将下面这两行的背景图跟滑块图路劲改成你自己的

<img src="output/background_with_gap.png" class="captcha-image" id="captcha-image">
<img src="output/slider_block.png" class="slider-block" id="slider-block" style="left: 0;">

 将这里的坐标替换成你自己生成的实际的坐标

const gapLeft = 176; // 将此值替换为你的缺口的实际横坐标
const gapTop = 64; // 将此值替换为你的缺口的实际纵坐标

如果要实现服务器验证也是一样的,创建个服务,然后从服务器获取图片跟x值,加载到上面的网页中即可,例如下面创建一个Ajax

// 滑块验证
let gapTop = 0;

function initializeCaptcha() {
    // 使用 Ajax 请求从后端获取 Base64 编码的图片和缺口坐标
    const xhr = new XMLHttpRequest();
    xhr.open('GET', '/Slide_verification/', true); // 替换为你的后端 API 端点
    xhr.onreadystatechange = function () {
        if (xhr.readyState === 4 && xhr.status === 200) {
            console.log(xhr.responseText)
            const response = JSON.parse(xhr.responseText);
            const backgroundBase64 = response.backgroundImage;
            const sliderBlockBase64 = response.sliderBlockImage;
            gapTop = response.gapTop; // 后端返回的缺口纵坐标

            const captchaImage = document.getElementById('captcha-image');
            const sliderBlock = document.getElementById('slider-block');

            captchaImage.src = `data:image/png;base64,${backgroundBase64}`;
            sliderBlock.src = `data:image/png;base64,${sliderBlockBase64}`;
            sliderBlock.style.top = gapTop + 'px';

            resetSlider();
        }
    };
    xhr.send();
}

const slider = document.getElementById('slider');
const sliderBlock = document.getElementById('slider-block');
let isDragging = false;
let startX = 0;

function resetSlider() {
    slider.style.left = '0px';
    sliderBlock.style.left = '0px';
}

slider.addEventListener('mousedown', function (e) {
    isDragging = true;
    startX = e.clientX - parseInt(slider.style.left, 10); // 记录鼠标按下时的初始位置
});

document.addEventListener('mouseup', function () {
    if (isDragging) {
        isDragging = false;
        const currentLeft = parseInt(slider.style.left, 10);
        // 发送验证请求到后端
        const xhr = new XMLHttpRequest();
        xhr.open('POST', '/Slide_verification/', true); // 替换为你的后端 API 端点
        xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
        xhr.setRequestHeader('X-CSRFToken', getCookie('csrftoken'));
        xhr.onreadystatechange = function () {
            if (xhr.readyState === 4 && xhr.status === 200) {
                const response = JSON.parse(xhr.responseText);
                if (response.success) {
                    alert('验证通过');
                    // 处理返回的token,比如存储在localStorage或sessionStorage
                    localStorage.setItem('sliderToken', response.token);
                    document.getElementById('error-message-1').textContent = response.message ;
                } else {
                    alert('验证失败');
                    document.getElementById('error-message-1').textContent = response.message ;
                    initializeCaptcha(); // 重新加载图像和初始化
                }
            }
        };
        xhr.send('gapLeft=' + currentLeft);
    }
});

document.addEventListener('mousemove', function (e) {
    if (isDragging) {
        const moveX = e.clientX - startX;
        if (moveX >= 0 && moveX <= (260 - 40)) {
            slider.style.left = moveX + 'px';
            sliderBlock.style.left = moveX + 'px';
        }
    }
});
// 初始化验证码
initializeCaptcha();

演示地址:皮卡熊

下期我将讲解文字点选验证

  • 11
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值