本文目录导读:

我来为您介绍几种在 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');
安全建议
- 密码加密:使用
password_hash()加密存储,不要明文存储 - 限制尝试次数:防止暴力破解
- 时间锁定:多次失败后锁定一段时间
- 使用 HTTPS:确保传输安全
- 输入验证:服务端也要验证输入的合法性
// 增加尝试次数限制的例子
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;
}
这种实现方案既简单又安全,可以根据您的具体需求进行调整,建议在实际生产环境中使用数据库存储并添加更多安全措施。