PHP 怎么实现图形锁

wen PHP项目 2

本文目录导读:

PHP 怎么实现图形锁

  1. 基于 JavaScript 的九宫格图形锁(推荐)
  2. 服务端验证模式
  3. 数据库存储版本
  4. 安全建议

我来为您介绍几种在 PHP 中实现图形锁的方案:

基于 JavaScript 的九宫格图形锁(推荐)

这种方案将图形锁的逻辑放在前端,PHP 只负责验证和处理结果。

HTML + JavaScript 部分

<!DOCTYPE html>
<html>
<head>
    <style>
        .lock-container {
            width: 300px;
            height: 300px;
            margin: 50px auto;
            position: relative;
            background: #f0f0f0;
        }
        .lock-point {
            width: 60px;
            height: 60px;
            border-radius: 50%;
            background: #ccc;
            position: absolute;
            display: flex;
            align-items: center;
            justify-content: center;
            cursor: pointer;
            transition: background 0.3s;
        }
        .lock-point.active {
            background: #4CAF50;
            color: white;
        }
        .lock-point.selected {
            background: #2196F3;
        }
        canvas {
            position: absolute;
            top: 0;
            left: 0;
        }
    </style>
</head>
<body>
    <div class="lock-container" id="lockContainer">
        <canvas id="lockCanvas"></canvas>
        <!-- 9个点 -->
        <div class="lock-point" data-index="0" style="left: 20px; top: 20px;">1</div>
        <div class="lock-point" data-index="1" style="left: 120px; top: 20px;">2</div>
        <div class="lock-point" data-index="2" style="left: 220px; top: 20px;">3</div>
        <div class="lock-point" data-index="3" style="left: 20px; top: 120px;">4</div>
        <div class="lock-point" data-index="4" style="left: 120px; top: 120px;">5</div>
        <div class="lock-point" data-index="5" style="left: 220px; top: 120px;">6</div>
        <div class="lock-point" data-index="6" style="left: 20px; top: 220px;">7</div>
        <div class="lock-point" data-index="7" style="left: 120px; top: 220px;">8</div>
        <div class="lock-point" data-index="8" style="left: 220px; top: 220px;">9</div>
    </div>
    <input type="hidden" id="pattern" name="pattern">
    <button onclick="submitPattern()">确认</button>
    <button onclick="resetPattern()">重置</button>
    <script>
        class PatternLock {
            constructor() {
                this.container = document.getElementById('lockContainer');
                this.canvas = document.getElementById('lockCanvas');
                this.ctx = this.canvas.getContext('2d');
                this.points = document.querySelectorAll('.lock-point');
                this.selectedPoints = [];
                this.isDrawing = false;
                this.initCanvas();
                this.bindEvents();
            }
            initCanvas() {
                this.canvas.width = 300;
                this.canvas.height = 300;
            }
            getPointPosition(point) {
                return {
                    x: parseFloat(point.style.left) + 30,
                    y: parseFloat(point.style.top) + 30
                };
            }
            bindEvents() {
                this.points.forEach(point => {
                    point.addEventListener('mousedown', (e) => this.handlePointClick(e, point));
                });
                this.container.addEventListener('mousemove', (e) => this.handleMouseMove(e));
                this.container.addEventListener('mouseup', () => this.handleMouseUp());
            }
            handlePointClick(e, point) {
                e.preventDefault();
                const index = parseInt(point.dataset.index);
                if (!this.selectedPoints.includes(index)) {
                    this.selectedPoints.push(index);
                    point.classList.add('selected');
                    this.drawPattern();
                }
                this.isDrawing = true;
            }
            handleMouseMove(e) {
                if (this.isDrawing) {
                    this.drawPattern();
                }
            }
            handleMouseUp() {
                this.isDrawing = false;
                this.drawPattern();
            }
            drawPattern() {
                this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);
                if (this.selectedPoints.length > 0) {
                    this.ctx.beginPath();
                    this.ctx.strokeStyle = '#2196F3';
                    this.ctx.lineWidth = 3;
                    const startPos = this.getPointPosition(this.points[this.selectedPoints[0]]);
                    this.ctx.moveTo(startPos.x, startPos.y);
                    for (let i = 1; i < this.selectedPoints.length; i++) {
                        const pos = this.getPointPosition(this.points[this.selectedPoints[i]]);
                        this.ctx.lineTo(pos.x, pos.y);
                    }
                    this.ctx.stroke();
                }
            }
            getPattern() {
                return this.selectedPoints.join('-');
            }
            reset() {
                this.selectedPoints = [];
                this.points.forEach(point => point.classList.remove('selected'));
                this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);
                this.isDrawing = false;
            }
        }
        const lock = new PatternLock();
        function submitPattern() {
            const pattern = lock.getPattern();
            if (pattern.length < 5) { // 至少连接3个点
                alert('至少需要连接3个点');
                return;
            }
            // 发送到服务器
            fetch('save_pattern.php', {
                method: 'POST',
                headers: {
                    'Content-Type': 'application/json',
                },
                body: JSON.stringify({ pattern: pattern })
            })
            .then(response => response.json())
            .then(data => {
                if (data.success) {
                    alert('图形锁设置成功!');
                } else {
                    alert('设置失败:' + data.message);
                }
            });
        }
        function resetPattern() {
            lock.reset();
        }
    </script>
</body>
</html>

PHP 后端处理

<?php
// save_pattern.php
session_start();
header('Content-Type: application/json');
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
    echo json_encode(['success' => false, 'message' => '请求方法错误']);
    exit;
}
$input = json_decode(file_get_contents('php://input'), true);
$pattern = isset($input['pattern']) ? $input['pattern'] : '';
// 验证图形锁的合法性
function validatePattern($pattern) {
    if (empty($pattern)) {
        return false;
    }
    $points = explode('-', $pattern);
    // 至少需要3个点
    if (count($points) < 3) {
        return false;
    }
    // 检查是否有重复的点
    if (count($points) !== count(array_unique($points))) {
        return false;
    }
    // 检查每个点是否合法(0-8)
    foreach ($points as $point) {
        if (!is_numeric($point) || intval($point) < 0 || intval($point) > 8) {
            return false;
        }
    }
    return true;
}
// 将图形密码加密存储
function hashPattern($pattern) {
    // 使用 password_hash 进行哈希处理
    return password_hash($pattern, PASSWORD_DEFAULT);
}
if (validatePattern($pattern)) {
    // 这里应该存储到数据库,这里用文件模拟存储
    $user_id = $_SESSION['user_id'] ?? 1; // 假设用户ID为1
    $hashed_pattern = hashPattern($pattern);
    // 存储到文件或数据库
    file_put_contents("pattern_{$user_id}.txt", $hashed_pattern);
    $response = [
        'success' => true,
        'message' => '设置成功',
        'pattern' => $pattern  // 注意:生产环境不要返回明文密码
    ];
} else {
    $response = [
        'success' => false,
        'message' => '无效的图形密码'
    ];
}
echo json_encode($response);

服务端验证模式

<?php
// verify_pattern.php
session_start();
header('Content-Type: application/json');
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
    echo json_encode(['success' => false, 'message' => '请求方法错误']);
    exit;
}
$input = json_decode(file_get_contents('php://input'), true);
$pattern = isset($input['pattern']) ? $input['pattern'] : '';
// 获取存储的图形密码
$user_id = $_SESSION['user_id'] ?? 1;
$stored_pattern_file = "pattern_{$user_id}.txt";
if (!file_exists($stored_pattern_file)) {
    echo json_encode(['success' => false, 'message' => '未找到存储的密码']);
    exit;
}
$stored_hash = file_get_contents($stored_pattern_file);
// 验证密码
if (password_verify($pattern, $stored_hash)) {
    $_SESSION['unlocked'] = true;
    echo json_encode(['success' => true, 'message' => '解锁成功']);
} else {
    echo json_encode(['success' => false, 'message' => '密码错误']);
}

数据库存储版本

<?php
// DatabasePatternLock.php
class DatabasePatternLock {
    private $db;
    public function __construct($host, $username, $password, $dbname) {
        $this->db = new PDO("mysql:host=$host;dbname=$dbname", $username, $password);
        $this->db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
    }
    // 设置图形锁
    public function savePattern($user_id, $pattern) {
        // 验证图形
        if (!$this->validatePattern($pattern)) {
            return ['success' => false, 'message' => '无效的图形'];
        }
        $hashed_pattern = password_hash($pattern, PASSWORD_DEFAULT);
        $sql = "INSERT INTO user_patterns (user_id, pattern_hash, created_at) 
                VALUES (:user_id, :pattern_hash, NOW())
                ON DUPLICATE KEY UPDATE pattern_hash = :pattern_hash, updated_at = NOW()";
        $stmt = $this->db->prepare($sql);
        $stmt->execute([
            ':user_id' => $user_id,
            ':pattern_hash' => $hashed_pattern
        ]);
        return ['success' => true];
    }
    // 验证图形锁
    public function verifyPattern($user_id, $pattern) {
        $sql = "SELECT pattern_hash FROM user_patterns WHERE user_id = :user_id";
        $stmt = $this->db->prepare($sql);
        $stmt->execute([':user_id' => $user_id]);
        $result = $stmt->fetch(PDO::FETCH_ASSOC);
        if ($result && password_verify($pattern, $result['pattern_hash'])) {
            return true;
        }
        return false;
    }
    // 验证图案格式
    private function validatePattern($pattern) {
        $points = explode('-', $pattern);
        if (count($points) < 3 || count($points) > 9) {
            return false;
        }
        // 检查是否有重复
        if (count($points) !== count(array_unique($points))) {
            return false;
        }
        // 检查范围
        foreach ($points as $point) {
            if (!is_numeric($point) || intval($point) < 0 || intval($point) > 8) {
                return false;
            }
        }
        return true;
    }
}
// 使用示例
$lock = new DatabasePatternLock('localhost', 'root', '', 'mydb');
$lock->savePattern($_SESSION['user_id'], '0-1-2-5-8');
$success = $lock->verifyPattern($_SESSION['user_id'], '0-1-2-5-8');

安全建议

  1. 密码加密:使用 password_hash() 加密存储,不要明文存储
  2. 限制尝试次数:防止暴力破解
  3. 时间锁定:多次失败后锁定一段时间
  4. 使用 HTTPS:确保传输安全
  5. 输入验证:服务端也要验证输入的合法性
// 增加尝试次数限制的例子
function checkAttempts($user_id) {
    $attempt_key = "failed_attempts_$user_id";
    $attempts = $_SESSION[$attempt_key] ?? 0;
    if ($attempts >= 5) {
        // 锁定用户
        $lock_time = $_SESSION['lock_time'] ?? 0;
        if (time() - $lock_time < 300) { // 5分钟
            return false;
        } else {
            // 重置
            $_SESSION[$attempt_key] = 0;
            unset($_SESSION['lock_time']);
            return true;
        }
    }
    return true;
}

这种实现方案既简单又安全,可以根据您的具体需求进行调整,建议在实际生产环境中使用数据库存储并添加更多安全措施。

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