PHP网站卡密门禁系统,支持一机一码,任意页面加验证
做了个付费内容站,用户想看教程得先付费。自己写了一套卡密验证方案,分享一下完整代码。
一、使用方式
在需要保护的页面顶部加两行代码:
php
require_once __DIR__ . '/keyt.cn.php'; if (!cardAuth()) exit;
用户访问该页面时自动弹出卡密验证框,输入正确卡密才能看到内容。
二、核心代码
keyt.cn.php完整代码(站长部署在网站根目录):
php
<?php /** * 卡密验证系统 * 在需要保护的页面顶部 require 此文件即可 */ // ========== 配置(替换成你自己的) ========== define('API_URL', 'https://你的域名/check.php'); define('APP_NAME', '你的应用名'); define('SIGN_KEY', '你的签名密钥'); // ============================================ if (session_status() === PHP_SESSION_NONE) { session_start(); } function cardAuth() { if (!isset($_SESSION['card_verified']) || $_SESSION['card_verified'] !== true) { redirectToVerify(); return false; } if (isset($_SESSION['card']) && !empty($_SESSION['card'])) { $result = heartbeatCheck($_SESSION['card']); if (!$result['valid']) { session_destroy(); session_start(); $_SESSION['error'] = $result['msg']; redirectToVerify(); return false; } $_SESSION['remain_days'] = $result['days']; $_SESSION['remain_minutes'] = $result['minutes']; } registerHeartbeatScript(); return true; } function redirectToVerify() { $redirect = isset($_SERVER['REQUEST_URI']) ? $_SERVER['REQUEST_URI'] : '/'; if (strpos($redirect, 'keyt.cn.php') === false) { header('Location: /keyt.cn.php?redirect=' . urlencode($redirect)); } else { header('Location: /keyt.cn.php'); } exit; } function registerHeartbeatScript() { register_shutdown_function(function() { $content = ob_get_clean(); if ($content === false) { echo '<script>' . getHeartbeatJS() . '</script>'; return; } $js = '<script>' . getHeartbeatJS() . '</script>'; $pos = strripos($content, '</body>'); if ($pos !== false) { $content = substr($content, 0, $pos) . $js . substr($content, $pos); } else { $content .= $js; } echo $content; }); ob_start(); } function getHeartbeatJS() { return <<<'JS' (function() { var CHECK_URL = '/keyt.cn.php?heartbeat=1&t=' + Date.now(); var HEARTBEAT_INTERVAL = 60000; function doHeartbeat() { fetch(CHECK_URL, { credentials: 'same-origin' }) .then(function(r) { return r.text(); }) .then(function(data) { if (data.trim() === 'EXPIRED') { alert('⚠️ 卡密已失效或过期,请重新验证'); window.location.href = '/keyt.cn.php?redirect=' + encodeURIComponent(window.location.pathname); } }) .catch(function() {}); } setInterval(doHeartbeat, HEARTBEAT_INTERVAL); document.addEventListener('visibilitychange', function() { if (!document.hidden) doHeartbeat(); }); })(); JS; } function verifyCard($card) { $mac = 'web_' . md5($_SERVER['REMOTE_ADDR'] ?? '0.0.0.0'); $url = API_URL . '?card=' . urlencode($card) . '&mac=' . urlencode($mac) . '&app=' . APP_NAME; $ch = curl_init(); curl_setopt($ch, CURLOPT_URL, $url); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); curl_setopt($ch, CURLOPT_TIMEOUT, 10); curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false); $response = curl_exec($ch); $error = curl_error($ch); curl_close($ch); if ($error) { return ['code' => 'error', 'msg' => '网络连接失败,请稍后再试']; } $pos = strpos($response, '|sign='); if ($pos !== false) { $response = substr($response, 0, $pos); } if (strpos($response, 'system_locked') !== false) { return ['code' => 'error', 'msg' => '系统维护中,请稍后再试']; } $parts = explode('|', $response); if ($parts[0] == 'ok') { $result = ['code' => 'ok', 'msg' => '验证成功']; if (isset($parts[1]) && $parts[1] == 'permanent') { $result['days'] = '永久'; $result['minutes'] = 999999; } elseif (isset($parts[2]) && is_numeric($parts[2])) { $result['days'] = $parts[2]; $result['minutes'] = isset($parts[3]) ? intval($parts[3]) : intval($parts[2]) * 1440; } else { $result['days'] = '未知'; $result['minutes'] = 0; } return $result; } else { $errorCode = isset($parts[1]) ? $parts[1] : '未知错误'; return ['code' => 'error', 'msg' => translateError($errorCode)]; } } function heartbeatCheck($card) { $mac = 'web_' . md5($_SERVER['REMOTE_ADDR'] ?? '0.0.0.0'); $url = API_URL . '?card=' . urlencode($card) . '&mac=' . urlencode($mac) . '&app=' . APP_NAME; $ch = curl_init(); curl_setopt($ch, CURLOPT_URL, $url); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); curl_setopt($ch, CURLOPT_TIMEOUT, 5); curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false); $response = curl_exec($ch); $error = curl_error($ch); curl_close($ch); if ($error) { return [ 'valid' => true, 'msg' => '网络波动', 'days' => $_SESSION['remain_days'] ?? '未知', 'minutes' => $_SESSION['remain_minutes'] ?? 0 ]; } $pos = strpos($response, '|sign='); if ($pos !== false) { $response = substr($response, 0, $pos); } if (strpos($response, 'system_locked') !== false) { return ['valid' => false, 'msg' => '系统已锁死,请联系管理员']; } $parts = explode('|', $response); if ($parts[0] == 'ok') { $days = '未知'; $minutes = 0; if (isset($parts[1]) && $parts[1] == 'permanent') { $days = '永久'; $minutes = 999999; } elseif (isset($parts[2]) && is_numeric($parts[2])) { $days = $parts[2]; $minutes = isset($parts[3]) ? intval($parts[3]) : intval($parts[2]) * 1440; } return ['valid' => true, 'msg' => '有效', 'days' => $days, 'minutes' => $minutes]; } else { $errorCode = isset($parts[1]) ? $parts[1] : '未知错误'; return ['valid' => false, 'msg' => translateError($errorCode)]; } } function translateError($code) { $map = [ 'missing_params' => '参数不完整', 'app_not_found' => '应用不存在', 'invalid_card' => '卡密无效,请检查是否输入正确', 'expired' => '卡密已过期', 'banned' => '卡密已被禁用', 'device_mismatch' => '设备不匹配', 'online_limit_reached' => '在线设备数已满', 'too_frequent' => '请求过于频繁,请稍后再试', 'system_locked' => '系统已锁死,请联系管理员', 'not_bound' => '卡密未绑定设备', 'unbind_disabled' => '该卡密不支持自助解绑', 'not_activated' => '卡密未激活', ]; return isset($map[$code]) ? $map[$code] : '验证失败:' . $code; } // ============================================================ // 以下代码仅在直接访问 keyt.cn.php 时执行(验证页面) // ============================================================ if (basename($_SERVER['PHP_SELF']) == 'keyt.cn.php' && !isset($_SERVER['HTTP_X_REQUESTED_WITH'])) { if (isset($_GET['heartbeat'])) { header('Content-Type: text/plain'); if (isset($_SESSION['card_verified']) && $_SESSION['card_verified'] === true && isset($_SESSION['card'])) { $result = heartbeatCheck($_SESSION['card']); echo $result['valid'] ? 'OK' : 'EXPIRED'; } else { echo 'EXPIRED'; } exit; } $error = $_SESSION['error'] ?? ''; unset($_SESSION['error']); if ($_SERVER['REQUEST_METHOD'] == 'POST' && isset($_POST['card'])) { $card = trim($_POST['card']); $redirect = isset($_POST['redirect']) ? $_POST['redirect'] : '/'; if (empty($card)) { $error = '请输入卡密'; } else { $result = verifyCard($card); if ($result['code'] == 'ok') { $_SESSION['card_verified'] = true; $_SESSION['card'] = $card; $_SESSION['verify_time'] = time(); $_SESSION['remain_days'] = $result['days'] ?? '永久'; $_SESSION['remain_minutes'] = $result['minutes'] ?? 0; header('Location: ' . $redirect); exit; } else { $error = $result['msg']; } } } $redirect = isset($_GET['redirect']) ? $_GET['redirect'] : (isset($_POST['redirect']) ? $_POST['redirect'] : '/'); ?> <!DOCTYPE html> <html lang="zh-CN"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>卡密验证</title> <style> * { margin:0; padding:0; box-sizing:border-box; } body { font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif; background: linear-gradient(145deg, #0f172a 0%, #1e293b 100%); min-height: 100vh; display: flex; justify-content: center; align-items: center; padding: 20px; } .verify-box { background: #ffffff; border-radius: 24px; padding: 48px 40px 40px; width: 440px; max-width: 100%; box-shadow: 0 30px 60px rgba(0,0,0,0.4); } .verify-box .icon { text-align:center; font-size:56px; margin-bottom:12px; } .verify-box h2 { text-align:center; font-size:24px; font-weight:700; color:#0f172a; margin-bottom:6px; } .verify-box .sub { text-align:center; font-size:14px; color:#94a3b8; margin-bottom:28px; } .verify-box .input-group { margin-bottom:18px; } .verify-box .input-group input { width:100%; padding:14px 18px; border:2px solid #e2e8f0; border-radius:12px; font-size:18px; outline:none; transition:border 0.2s; background:#f8fafc; letter-spacing:2px; font-family:monospace; text-align:center; } .verify-box .input-group input:focus { border-color:#4f46e5; background:#ffffff; } .verify-box .input-group .hint { font-size:12px; color:#94a3b8; margin-top:6px; text-align:center; } .verify-box .btn { width:100%; padding:14px; border:none; border-radius:12px; font-size:17px; font-weight:700; color:#fff; background:#4f46e5; cursor:pointer; transition:background 0.2s, transform 0.1s; } .verify-box .btn:hover { background:#4338ca; } .verify-box .btn:active { transform:scale(0.97); } .verify-box .btn:disabled { opacity:0.6; cursor:not-allowed; } .verify-box .error { background:#fef2f2; color:#dc2626; padding:10px 14px; border-radius:10px; font-size:14px; margin-bottom:16px; text-align:center; border:1px solid #fca5a5; } .verify-box .footer { text-align:center; font-size:12px; color:#94a3b8; margin-top:20px; padding-top:16px; border-top:1px solid #f1f5f9; } .verify-box .footer a { color:#4f46e5; text-decoration:none; } .loading { display:none; text-align:center; margin:12px 0; color:#94a3b8; font-size:14px; } .loading.show { display:block; } .spinner { display:inline-block; width:20px; height:20px; border:3px solid #e2e8f0; border-top-color:#4f46e5; border-radius:50%; animation:spin 0.6s linear infinite; vertical-align:middle; margin-right:8px; } @keyframes spin { to { transform:rotate(360deg); } } </style> </head> <body> <div class="verify-box"> <div class="icon">🔐</div> <h2>请输入卡密</h2> <p class="sub">验证通过后即可正常访问</p> <?php if (!empty($error)): ?> <div class="error">❌ <?php echo htmlspecialchars($error); ?></div> <?php endif; ?> <form method="post" id="verifyForm"> <input type="hidden" name="redirect" value="<?php echo htmlspecialchars($redirect); ?>"> <div class="input-group"> <input type="text" id="card" name="card" placeholder="请在此输入卡密" autofocus required> <div class="hint">💡 卡密区分大小写,请准确输入</div> </div> <div id="loading" class="loading"> <span class="spinner"></span> 验证中,请稍候... </div> <button type="submit" class="btn" id="submitBtn">✅ 验证卡密</button> </form> <div class="footer"> 遇到问题?请联系 <a href="#">管理员</a> </div> </div> <script> document.getElementById('verifyForm').addEventListener('submit', function() { var btn = document.getElementById('submitBtn'); var loading = document.getElementById('loading'); var card = document.getElementById('card').value.trim(); if (!card) { alert('请输入卡密'); return false; } btn.disabled = true; btn.textContent = '⏳ 验证中...'; loading.classList.add('show'); return true; }); document.addEventListener('DOMContentLoaded', function() { document.getElementById('card').focus(); }); </script> </body> </html> <?php exit; } ?>三、部署说明
将上述代码保存为
keyt.cn.php,上传到网站根目录在需要保护的页面顶部添加两行代码:
php
require_once __DIR__ . '/keyt.cn.php'; if (!cardAuth()) exit;
后台管理:可生成、禁用卡密,查看使用记录和在线统计
四、完整源码
完整项目源码(含管理后台、API接口、多语言对接示例)已开源,可在卡密通官网https://www.keyt.cn下载。
