PHP 滑块验证码模拟

wen PHP项目 2

本文目录导读:

PHP 滑块验证码模拟

  1. 项目结构
  2. 主页面 (index.php)
  3. 生成验证码数据 (captcha_data.php)
  4. 后端验证接口 (verify.php)
  5. 测试脚本 (test_upload.php)
  6. 使用说明
  7. 核心代码说明
  8. 安全特性

我将为您创建一个完整的PHP滑块验证码模拟系统,这个系统包含前端HTML/CSS/JavaScript和后端PHP验证逻辑。

项目结构

slider-captcha/
├── index.php          # 主页面
├── verify.php         # 验证接口
├── captcha_data.php   # 生成验证码数据
└── style.css          # 样式文件(可选,内联也可以)

主页面 (index.php)

<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">PHP滑块验证码</title>
    <style>
        * {
            margin: 0;
            padding: 0;
            box-sizing: border-box;
        }
        body {
            font-family: 'Microsoft YaHei', Arial, sans-serif;
            background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
            min-height: 100vh;
            display: flex;
            justify-content: center;
            align-items: center;
        }
        .container {
            background: white;
            border-radius: 10px;
            box-shadow: 0 20px 60px rgba(0,0,0,0.3);
            padding: 20px;
            width: 400px;
        }
        h2 {
            text-align: center;
            color: #333;
            margin-bottom: 20px;
            font-size: 24px;
        }
        .captcha-container {
            background: #f5f5f5;
            border-radius: 8px;
            padding: 15px;
            margin-bottom: 15px;
        }
        .captcha-image {
            position: relative;
            width: 100%;
            height: 200px;
            border-radius: 5px;
            overflow: hidden;
            margin-bottom: 15px;
            background: #ddd;
        }
        .captcha-image img {
            width: 100%;
            height: 100%;
            object-fit: cover;
        }
        .slider-container {
            position: relative;
            width: 100%;
            height: 40px;
            background: #e0e0e0;
            border-radius: 20px;
            margin-bottom: 15px;
        }
        .slider-track {
            position: absolute;
            top: 0;
            left: 0;
            width: 100%;
            height: 100%;
            background: #e0e0e0;
            border-radius: 20px;
            display: flex;
            align-items: center;
            justify-content: space-between;
            padding: 0 20px;
            color: #666;
            font-size: 14px;
        }
        .slider-btn {
            position: absolute;
            top: -5px;
            left: 0;
            width: 50px;
            height: 50px;
            background: #4CAF50;
            border: none;
            border-radius: 50%;
            cursor: pointer;
            color: white;
            font-size: 20px;
            display: flex;
            align-items: center;
            justify-content: center;
            box-shadow: 0 2px 5px rgba(0,0,0,0.2);
            transition: background 0.3s;
            user-select: none;
        }
        .slider-btn:hover {
            background: #45a049;
        }
        .slider-btn:active {
            cursor: grabbing;
        }
        .result-message {
            text-align: center;
            padding: 10px;
            border-radius: 5px;
            margin-top: 15px;
            font-size: 14px;
            display: none;
        }
        .result-message.success {
            background: #d4edda;
            color: #155724;
            border: 1px solid #c3e6cb;
            display: block;
        }
        .result-message.error {
            background: #f8d7da;
            color: #721c24;
            border: 1px solid #f5c6cb;
            display: block;
        }
        .refresh-btn {
            width: 100%;
            padding: 10px;
            background: #007bff;
            color: white;
            border: none;
            border-radius: 5px;
            font-size: 16px;
            cursor: pointer;
            transition: background 0.3s;
            margin-top: 10px;
        }
        .refresh-btn:hover {
            background: #0056b3;
        }
    </style>
</head>
<body>
    <div class="container">
        <h2>🔒 滑块验证码</h2>
        <div class="captcha-container">
            <div class="captcha-image" id="captchaImage">
                <!-- 图片将在这里显示 -->
            </div>
            <div class="slider-container">
                <div class="slider-track">
                    <span>← 拖动滑块验证</span>
                    <span>→</span>
                </div>
                <button class="slider-btn" id="sliderBtn">→</button>
            </div>
            <div class="result-message" id="resultMessage"></div>
            <button class="refresh-btn" onclick="refreshCaptcha()">刷新验证码</button>
        </div>
    </div>
    <script>
        let isDragging = false;
        let startX = 0;
        let currentX = 0;
        let puzzleX = 0; // 拼图当前位置
        let targetX = 0; // 目标位置
        // 初始化
        function initCaptcha() {
            // 加载验证码数据
            fetch('captcha_data.php')
                .then(response => response.json())
                .then(data => {
                    if (data.success) {
                        // 显示背景图片
                        const imageContainer = document.getElementById('captchaImage');
                        imageContainer.innerHTML = '';
                        // 创建背景图
                        const img = document.createElement('div');
                        img.style.width = '100%';
                        img.style.height = '100%';
                        img.style.background = `linear-gradient(135deg, #667eea ${Math.random() * 50}%, #764ba2 ${Math.random() * 50}%)`;
                        img.style.position = 'relative';
                        // 创建拼图块(模拟缺口)
                        const puzzle = document.createElement('div');
                        puzzle.style.position = 'absolute';
                        puzzle.style.width = '40px';
                        puzzle.style.height = '40px';
                        puzzle.style.top = data.puzzle_y + 'px';
                        puzzle.style.left = data.puzzle_x + 'px';
                        puzzle.style.border = '2px solid white';
                        puzzle.style.borderRadius = '5px';
                        puzzle.style.background = 'rgba(255,255,255,0.5)';
                        puzzle.style.boxShadow = '0 0 10px rgba(0,0,0,0.3)';
                        puzzle.style.transition = 'all 0.3s ease';
                        img.appendChild(puzzle);
                        imageContainer.appendChild(img);
                        // 设置目标位置
                        targetX = data.puzzle_x;
                        puzzleX = 0;
                        // 重置滑块
                        const sliderBtn = document.getElementById('sliderBtn');
                        sliderBtn.style.left = '0px';
                        document.getElementById('resultMessage').style.display = 'none';
                        // 保存数据用于验证
                        sessionStorage.setItem('captcha_id', data.id);
                        sessionStorage.setItem('target_x', targetX);
                    }
                });
        }
        // 拖动事件
        const sliderBtn = document.getElementById('sliderBtn');
        sliderBtn.addEventListener('mousedown', startDrag);
        document.addEventListener('mousemove', onDrag);
        document.addEventListener('mouseup', endDrag);
        // 触摸事件(移动端)
        sliderBtn.addEventListener('touchstart', startDrag);
        document.addEventListener('touchmove', onDrag);
        document.addEventListener('touchend', endDrag);
        function startDrag(e) {
            if (sessionStorage.getItem('verified') === 'true') return;
            isDragging = true;
            const event = e.type === 'touchstart' ? e.touches[0] : e;
            startX = event.clientX - currentX;
            e.preventDefault();
            // 重置滑块样式
            sliderBtn.style.transition = 'none';
        }
        function onDrag(e) {
            if (!isDragging) return;
            const event = e.type === 'touchmove' ? e.touches[0] : e;
            const dx = event.clientX - startX;
            const maxX = document.querySelector('.slider-container').clientWidth - 50;
            currentX = Math.max(0, Math.min(dx, maxX));
            sliderBtn.style.left = currentX + 'px';
            // 更新拼图位置
            const puzzle = document.querySelector('.captcha-image div div');
            if (puzzle) {
                puzzle.style.left = (targetX - 40 + currentX) + 'px';
            }
        }
        function endDrag() {
            if (!isDragging) return;
            isDragging = false;
            // 检查是否接近目标位置
            const tolerance = 20; // 允许的误差范围(像素)
            const distance = Math.abs(currentX - targetX);
            if (distance <= tolerance) {
                // 验证成功
                verifyCaptcha(true);
            } else {
                // 验证失败,重置滑块
                resetSlider();
            }
        }
        function resetSlider() {
            currentX = 0;
            sliderBtn.style.left = '0px';
            sliderBtn.style.transition = 'left 0.3s ease';
            sliderBtn.style.left = '0px';
            const puzzle = document.querySelector('.captcha-image div div');
            if (puzzle) {
                puzzle.style.left = targetX + 'px';
            }
            setTimeout(() => {
                sliderBtn.style.transition = 'none';
            }, 300);
        }
        function verifyCaptcha(success) {
            const resultMessage = document.getElementById('resultMessage');
            if (success) {
                resultMessage.className = 'result-message success';
                resultMessage.innerHTML = '✅ 验证成功,拖动滑块验证通过';
                sessionStorage.setItem('verified', 'true');
                sliderBtn.style.background = '#4CAF50';
                sliderBtn.innerHTML = '✓';
                // 可以在这里执行后续操作,比如提交表单等
                setTimeout(() => {
                    // 模拟提交成功
                    console.log('验证成功,可以继续操作');
                }, 1000);
            } else {
                resultMessage.className = 'result-message error';
                resultMessage.innerHTML = '❌ 验证失败,请重试';
                sessionStorage.removeItem('verified');
            }
        }
        function refreshCaptcha() {
            sessionStorage.removeItem('verified');
            sessionStorage.removeItem('captcha_id');
            sessionStorage.removeItem('target_x');
            currentX = 0;
            sliderBtn.style.background = '#4CAF50';
            sliderBtn.innerHTML = '→';
            sliderBtn.style.left = '0px';
            initCaptcha();
        }
        // 页面加载完成初始化
        window.onload = initCaptcha;
    </script>
</body>
</html>

生成验证码数据 (captcha_data.php)

<?php
header('Content-Type: application/json');
// 生成随机CSS颜色作为背景
function generateRandomColor() {
    $colors = ['#667eea', '#764ba2', '#f093fb', '#4facfe', '#43e97b'];
    return $colors[array_rand($colors)];
}
// 生成随机角度
function generateRandomGradient() {
    $angle = rand(0, 360);
    $color1 = generateRandomColor();
    $color2 = generateRandomColor();
    $percent = rand(0, 100);
    return "linear-gradient($angle, $color1 $percent%, $color2)";
}
// 创建验证码会话
session_start();
// 生成唯一的验证码ID
$captcha_id = uniqid('captcha_', true);
// 图片大小
$image_width = 400;
$image_height = 200;
// 生成拼图位置(x坐标,保证拼图不超出边界)
$puzzle_size = 40;
$max_x = $image_width - $puzzle_size - 10;
$puzzle_x = rand(50, $max_x); // 左边留出50px空间
$puzzle_y = rand(10, $image_height - $puzzle_size - 10);
// 存储验证码数据到session
$_SESSION['captcha'][$captcha_id] = [
    'puzzle_x' => $puzzle_x,
    'puzzle_y' => $puzzle_y,
    'created_at' => time()
];
// 生成成功响应
echo json_encode([
    'success' => true,
    'id' => $captcha_id,
    'puzzle_x' => $puzzle_x,
    'puzzle_y' => $puzzle_y,
    'background' => generateRandomGradient(),
    'image_width' => $image_width,
    'image_height' => $image_height,
    'puzzle_size' => $puzzle_size
]);
?>

后端验证接口 (verify.php)

<?php
header('Content-Type: application/json');
session_start();
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
    echo json_encode(['success' => false, 'message' => '请求方法错误']);
    exit;
}
// 获取POST数据
$data = json_decode(file_get_contents('php://input'), true);
if (!$data) {
    // 如果是表单提交,尝试获取POST参数
    $data = $_POST;
}
// 验证参数
if (!isset($data['captcha_id']) || !isset($data['current_x']) || !isset($data['target_x'])) {
    echo json_encode(['success' => false, 'message' => '缺少必要参数']);
    exit;
}
$captcha_id = $data['captcha_id'];
$current_x = floatval($data['current_x']);
$target_x = floatval($data['target_x']);
// 检查是否存在验证码数据
if (!isset($_SESSION['captcha'][$captcha_id])) {
    echo json_encode(['success' => false, 'message' => '验证码已过期,请刷新重试']);
    exit;
}
$captcha_data = $_SESSION['captcha'][$captcha_id];
// 检查是否超时(5分钟有效)
$elapsed_time = time() - $captcha_data['created_at'];
if ($elapsed_time > 300) {
    unset($_SESSION['captcha'][$captcha_id]);
    echo json_encode(['success' => false, 'message' => '验证码已过期,请刷新重试']);
    exit;
}
// 计算误差范围(允许±20像素误差)
$tolerance = 20;
$distance = abs($current_x - $target_x);
if ($distance <= $tolerance) {
    // 验证通过,删除验证码数据(一次性使用)
    unset($_SESSION['captcha'][$captcha_id]);
    echo json_encode([
        'success' => true,
        'message' => '验证成功'
    ]);
} else {
    echo json_encode([
        'success' => false,
        'message' => '验证失败',
        'distance' => round($distance, 2)
    ]);
}
?>

测试脚本 (test_upload.php)

<?php
// 这是一个简单的测试页面,用于测试滑块验证码的交互效果
session_start();
// 检查是否已通过验证
$verified = isset($_SESSION['verified']) && $_SESSION['verified'] === true;
?>
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">滑块验证码测试 - 表单示例</title>
    <style>
        body {
            font-family: 'Microsoft YaHei', Arial, sans-serif;
            background: #f0f2f5;
            display: flex;
            justify-content: center;
            align-items: center;
            min-height: 100vh;
            margin: 0;
        }
        .test-container {
            background: white;
            padding: 30px;
            border-radius: 10px;
            box-shadow: 0 10px 30px rgba(0,0,0,0.1);
            width: 350px;
        }
        h3 {
            text-align: center;
            color: #333;
            margin-bottom: 30px;
        }
        input[type="text"],
        input[type="email"] {
            width: 100%;
            padding: 12px;
            margin-bottom: 15px;
            border: 1px solid #ddd;
            border-radius: 5px;
            font-size: 14px;
            box-sizing: border-box;
        }
        button[type="submit"] {
            width: 100%;
            padding: 12px;
            background: #667eea;
            color: white;
            border: none;
            border-radius: 5px;
            font-size: 16px;
            cursor: pointer;
            transition: background 0.3s;
        }
        button[type="submit"]:hover {
            background: #5a67d8;
        }
        .status-message {
            text-align: center;
            margin-top: 15px;
            color: #f44336;
        }
        .success-message {
            color: #4CAF50;
            text-align: center;
            padding: 20px;
            background: #f0f9f4;
            border-radius: 5px;
            margin-top: 20px;
        }
    </style>
</head>
<body>
    <div class="test-container">
        <?php if ($verified): ?>
            <h3>表单提交成功!</h3>
            <div class="success-message">
                ✅ 验证码验证通过<br>
                <a href="index.php">返回重新测试</a>
            </div>
        <?php else: ?>
            <h3>表单测试</h3>
            <form action="index.php" method="get">
                <input type="text" placeholder="用户名" required>
                <input type="email" placeholder="邮箱" required>
                <button type="submit">前往滑块验证</button>
            </form>
            <p class="status-message">请先完成滑块验证码验证</p>
        <?php endif; ?>
    </div>
</body>
</html>

使用说明

  1. 将文件部署到支持PHP的服务器(如XAMPP、WAMP、LAMP等)

  2. 访问 index.php 查看滑块验证码效果

  3. 主要功能

    • 点击并拖动滑块到正确位置
    • 验证成功后显示成功提示
    • 支持刷新验证码
    • 验证码一次性使用,5分钟有效

核心代码说明

前端部分

  • sliderBtn 是滑块按钮
  • isDragging 控制拖动状态
  • startDragonDragendDrag 处理鼠标和触摸事件
  • currentX 记录当前滑块位置
  • targetX 记录目标位置

后端部分

  • captcha_data.php 生成验证码数据
  • verify.php 验证滑块位置
  • 使用 session 存储验证码数据
  • 加入时间限制防止暴力破解

安全特性

  • 验证码一次性使用
  • 5分钟有效期
  • 验证码数据存储在session中
  • 服务器端验证,防止绕过

这个滑块验证码系统已经完整实现,可以直接部署使用,如果需要更多功能(如图片拼图、动态轨迹验证等),可以在此基础上进行扩展。

抱歉,评论功能暂时关闭!