前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >PHP实现一个内容阅后即焚平台

PHP实现一个内容阅后即焚平台

作者头像
C4rpeDime
发布2024-10-16 18:29:23
690
发布2024-10-16 18:29:23
举报
文章被收录于专栏:黑白安全

本文档介绍了一个简易的“阅后即焚”平台的技术实现,涵盖了前端界面、后端逻辑以及内容查看的实现。该平台允许用户提交内容并设置内容的销毁条件(如时间限制或访问次数限制)。平台会在到达条件时自动销毁该内容。

1. 平台简介

“阅后即焚”平台的核心功能是允许用户创建临时的文本内容,并在设定条件满足后销毁这些内容。用户可以通过时间或最大访问次数来控制内容的存活时间。该平台还支持对内容设置访问密码以增强隐私性。 平台的功能流程分为三部分:

  1. 前端提交内容和设置条件
  2. 后端保存内容并处理销毁逻辑
  3. 查看内容和执行销毁

2. 前端页面分析

前端主要负责内容提交的表单展示、与用户的交互以及结果展示。

2.1 页面结构

前端代码中使用了HTML5、Bootstrap和jQuery库来实现响应式布局和交互功能。页面包括了一个简单的导航栏、内容提交表单、结果展示区域,以及一个固定的页脚。 主要代码:

代码语言:javascript
复制
<form id="pasteForm">
    <div class="form-group">
        <label for="content">内容</label>
        <textarea class="form-control" id="content" name="content" rows="5" required></textarea>
    </div>
    <div class="form-group">
        <label for="password">密码(可选)</label>
        <input type="text" class="form-control" id="password" name="password">
    </div>
    <div class="form-group">
        <label>过期设置</label>
        <div class="form-row">
            <div class="col-md-6">
                <div class="form-check">
                    <input class="form-check-input" type="radio" name="expire_condition" value="time" id="expireConditionTime" checked>
                    <label class="form-check-label" for="expireConditionTime">销毁时间(分钟)</label>
                    <input type="number" name="expiration_minutes" class="form-control mt-2" placeholder="例如: 60">
                </div>
            </div>
            <div class="col-md-6">
                <div class="form-check">
                    <input class="form-check-input" type="radio" name="expire_condition" value="views" id="expireConditionViews">
                    <label class="form-check-label" for="expireConditionViews">最大访问次数</label>
                    <input type="number" name="max_views" class="form-control mt-2" placeholder="例如: 10">
                </div>
            </div>
        </div>
    </div>
    <div class="form-group">
        <button type="submit" class="btn btn-primary">创建</button>
    </div>
</form>

2.2 交互逻辑

使用jQuery进行AJAX请求,将表单数据提交到后端的create_paste.php。前端的表单提交事件被拦截,通过AJAX发送请求并处理返回的结果。

代码语言:javascript
复制
$('#pasteForm').submit(function(e) {
    e.preventDefault();

    $.ajax({
        type: 'POST',
        url: 'create_paste.php',
        data: $(this).serialize(),
        success: function(response) {
            // 成功后处理响应数据
        }
    });
});

前端会根据后端返回的结果(如创建成功与否)动态显示内容,并且支持一键复制功能。

3. 后端逻辑分析

后端代码使用PHP和MySQL数据库进行内容的保存和验证。主要功能包括:

  1. 验证并保存用户输入的内容。
  2. 根据设定的条件(时间或访问次数)销毁内容。
  3. 对内容进行访问控制,包括密码保护。

3.1 数据库保存逻辑

用户提交的内容以及相关的销毁条件会通过AJAX请求传递到后端的create_paste.php脚本。以下是核心的后端处理逻辑:

代码语言:javascript
复制
$content = $_POST['content'];
$password = $_POST['password'] ? password_hash($_POST['password'], PASSWORD_DEFAULT) : null;
$expiration_minutes = ($_POST['expire_condition'] == 'time') ? $_POST['expiration_minutes'] : null;
$max_views = ($_POST['expire_condition'] == 'views') ? $_POST['max_views'] : null;
$identifier = bin2hex(random_bytes(8)); // 生成唯一标识符

然后使用PDO连接数据库并将用户的内容保存到pastes表中。

代码语言:javascript
复制
$db = connect();
$stmt = $db->prepare("INSERT INTO pastes (content, password, expiration_minutes, max_views, identifier) VALUES (?, ?, ?, ?, ?)");
$stmt->execute([$content, $password, $expiration_minutes, $max_views, $identifier]);

返回成功后,平台会返回一个包含访问链接的JSON响应:

代码语言:javascript
复制
$response = [
    'success' => true,
    'link' => $link,
    'expiration_minutes' => $expiration_minutes,
    'max_views' => $max_views,
    'password_set' => $password ? $_POST['password'] : null
];
echo json_encode($response);

3.2 销毁逻辑

内容的销毁是通过检查当前时间或访问次数来实现的。访问内容时,后端脚本会根据内容的创建时间或访问次数进行判断。如果条件已满足,自动删除对应的内容。

代码语言:javascript
复制
if ($paste['expiration_minutes'] !== null) {
    $createdTime->modify("+{$paste['expiration_minutes']} minutes");
    if ($currentTime > $createdTime) {
        // 内容过期,删除记录
        $db->prepare("DELETE FROM pastes WHERE identifier = :identifier")->execute([':identifier' => $identifier]);
        exit('该内容已过期。');
    }
}

同样,访问次数的限制也是通过数据库字段max_viewscurrent_views来实现的。

4. 内容查看逻辑

查看内容时,后端通过view.php页面接收访问请求。用户访问内容的URL带有唯一标识符id,通过它查询数据库中的对应记录。

4.1 验证和访问控制

如果内容设置了密码保护,系统会要求用户输入密码。后端使用password_verify验证用户输入的密码是否匹配。

代码语言:javascript
复制
if ($paste['password']) {
    if ($_SERVER['REQUEST_METHOD'] == 'POST') {
        $entered_password = $_POST['password'];
        if (!password_verify($entered_password, $paste['password'])) {
            exit('密码不正确。');
        }
    } else {
        // 显示密码输入表单
    }
}

4.2 内容显示

验证通过后,后端会增加该内容的访问次数并展示内容:

代码语言:javascript
复制
$db->prepare("UPDATE pastes SET current_views = current_views + 1 WHERE identifier = :identifier")->execute([':identifier' => $identifier]);

内容以HTML的形式呈现在用户界面上:

代码语言:javascript
复制
<pre><?php echo htmlspecialchars($paste['content']); ?></pre>

5. 总结

本阅后即焚平台实现了一个简单而有效的系统,用户可以提交并设定销毁条件(时间或访问次数)来保护隐私。核心逻辑包括:

  • 前端:通过AJAX提交数据并处理返回结果。
  • 后端:验证并存储用户数据,处理销毁逻辑。
  • 查看:用户通过唯一链接查看内容,并根据条件自动销毁。 这种实现方案可以有效用于临时信息分享、敏感数据传递等场景,确保数据不会长期存储,提升安全性。

6. 成品展示

微信图片_20241016031626.png
微信图片_20241016031626.png
微信图片_20241016034750.png
微信图片_20241016034750.png

7. 完整代码

index.php

代码语言:javascript
复制
<?php
if (!file_exists('installed.lock')) {
    header('Location: install.php');
    exit;
}

require 'config.php';
?>
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title><?php echo SITE_TITLE; ?> - 阅后即焚</title>
    <link rel="stylesheet" href="//cdn.staticfile.net/font-awesome/4.7.0/css/font-awesome.min.css">
    <link rel="stylesheet" href="//cdn.staticfile.net/twitter-bootstrap/4.6.1/css/bootstrap.min.css">
    <link rel="stylesheet" href="https://www.hacktips.cn/static/css/style.css?v=1002">
</head>
<body>
  <nav class="navbar sticky-top navbar-expand-lg navbar-light bg-white border-bottom" id="navbar">
    <div class="container big-nav">
      <a class="navbar-brand" href="/">
        <?php echo SITE_TITLE; ?>
      </a>
      <button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation">
        <span class="navbar-toggler-icon"></span>
      </button>
      <div class="collapse navbar-collapse" id="navbarSupportedContent">
      
        <ul class="navbar-nav ml-auto">
               <li class="nav-item"><a class="nav-link" href="/">首页</a></li>
         
        </ul>
      </div>
    </div>
  </nav>

  <div id="main">
    <div class="col-xs-12 col-sm-10 col-md-8 col-lg-4 center-block" style="float: none;">
      <div class="col-12 mt-0 mt-sm-3">
        <div class="container">
          <h1 class="mt-5"><?php echo SITE_TITLE; ?> - 阅后即焚</h1>
         <form id="pasteForm">
    <div class="form-group">
        <label for="content">内容</label>
        <textarea class="form-control" id="content" name="content" rows="5" required></textarea>
    </div>
    <div class="form-group">
        <label for="password">密码(可选)</label>
        <input type="text" class="form-control" id="password" name="password">
    </div>
    <div class="form-group">
        <label>过期设置</label>
        <div class="form-row">
            <div class="col-md-6">
                <div class="form-check">
                    <input class="form-check-input" type="radio" name="expire_condition" value="time" id="expireConditionTime" checked>
                    <label class="form-check-label" for="expireConditionTime">销毁时间(分钟)</label>
                    <input type="number" name="expiration_minutes" class="form-control mt-2" placeholder="例如: 60">
                </div>
            </div>
            <div class="col-md-6">
                <div class="form-check">
                    <input class="form-check-input" type="radio" name="expire_condition" value="views" id="expireConditionViews">
                    <label class="form-check-label" for="expireConditionViews">最大访问次数</label>
                    <input type="number" name="max_views" class="form-control mt-2" placeholder="例如: 10">
                </div>
            </div>
        </div>
    </div>
    <div class="form-group">
        <button type="submit" class="btn btn-primary">创建<?php echo SITE_TITLE; ?></button>
        <button type="button" id="copyButton" class="btn btn-secondary ml-3" style="display:none;">复制<?php echo SITE_TITLE; ?></button>
    </div>
</form>

          <div id="result" class="mt-4"></div>
        </div>
      </div>
    </div>
  </div>

  <footer class="footer card-footer mt-3" id="footer">
    <span>Copyright &copy; 2024 <?php echo SITE_TITLE; ?> All Rights Reserved.</span>
  </footer>

  <script src="//cdn.staticfile.net/jquery/3.6.1/jquery.min.js"></script>
  <script src="//cdn.staticfile.net/twitter-bootstrap/4.6.1/js/bootstrap.min.js"></script>
  <script>
    function fix_footer(){
        var body_height = document.getElementById("navbar").offsetHeight + document.getElementById("main").offsetHeight;
        var foot_height = document.getElementById("footer").offsetHeight;
        var win_height = window.innerHeight;
        if(body_height + foot_height > win_height){
            document.getElementById("footer").className += ' position-relative';
        }
    }
    fix_footer();

        $('#pasteForm').submit(function(e) {
        e.preventDefault();

        $.ajax({
            type: 'POST',
            url: 'create_paste.php',
            data: $(this).serialize(),
            success: function(response) {
                let resultDiv = $('#result');
                resultDiv.empty(); // 清空之前的错误信息

                if (response.success) {
                    // 准备要覆盖到 textarea 的内容
                    let contentToCopy = '<?php echo SITE_TITLE; ?>地址: ' + response.link + '\n';
                    if (response.password_set) {
                        contentToCopy += '密码: ' + response.password_set + '\n';
                    }
                    if (response.expiration_minutes) {
                        contentToCopy += '到期时间: ' + response.expiration_minutes + ' 分钟后\n';
                    }
                    if (response.max_views) {
                        contentToCopy += '最大访问次数: ' + response.max_views + '\n';
                    }

                    // 将内容覆盖到 textarea 中
                    $('#content').val(contentToCopy);

                    // 显示并启用复制按钮
                    $('#copyButton').show().off('click').on('click', function() {
                        $('#content').select();
                        document.execCommand('copy');
                        alert('内容已复制到剪贴板!');
                    });

                } else {
                    // 生成失败时,显示错误信息
                    resultDiv.text('<?php echo SITE_TITLE; ?>创建失败: ' + response.message);
                    $('#copyButton').hide(); // 隐藏复制按钮
                }
            }
        });
    });
  </script>
</body>
</html>

install.php

代码语言:javascript
复制
<?php
if (file_exists('installed.lock')) {
    echo "系统已安装。如果需要重新安装,请删除 installed.lock 文件后重新访问本页面。";
    exit;
}

if ($_SERVER['REQUEST_METHOD'] == 'POST') {
    $db_host = $_POST['db_host'];
    $db_name = $_POST['db_name'];
    $db_user = $_POST['db_user'];
    $db_pass = $_POST['db_pass'];
    $site_title = $_POST['site_title'];

    $configContent = "<?php\n";
    $configContent .= "define('DB_HOST', '{$db_host}');\n";
    $configContent .= "define('DB_NAME', '{$db_name}');\n";
    $configContent .= "define('DB_USER', '{$db_user}');\n";
    $configContent .= "define('DB_PASS', '{$db_pass}');\n";
    $configContent .= "define('SITE_TITLE', '{$site_title}');\n";
    file_put_contents('config.php', $configContent);

    include 'database.php';
    try {
        $db = connect();
        
        // 如果存在则覆盖现有表
        $db->exec("DROP TABLE IF EXISTS pastes");

        $query = "
            CREATE TABLE pastes (
                id INT AUTO_INCREMENT PRIMARY KEY,
                content TEXT NOT NULL,
                password VARCHAR(255),
                expiration_minutes INT,
                max_views INT,
                current_views INT DEFAULT 0,
                identifier VARCHAR(16) NOT NULL UNIQUE,
                created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
            )
        ";
        $db->exec($query);
        file_put_contents('installed.lock', 'installed');
        echo "安装成功。<a href='index.php'>前往主页</a>";
    } catch (Exception $e) {
        echo '安装失败: ' . $e->getMessage();
    }
    exit;
}
?>

<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <title>安装程序</title>
    <link href="https://cdn.bootcdn.net/ajax/libs/twitter-bootstrap/4.5.2/css/bootstrap.min.css" rel="stylesheet">
</head>
<body>
<div class="container">
    <h1 class="mt-5">安装程序</h1>
    <form method="post">
        <div class="form-group">
            <label for="db_host">数据库主机</label>
            <input type="text" class="form-control" id="db_host" name="db_host" required>
        </div>
        <div class="form-group">
            <label for="db_name">数据库名称</label>
            <input type="text" class="form-control" id="db_name" name="db_name" required>
        </div>
        <div class="form-group">
            <label for="db_user">数据库用户</label>
            <input type="text" class="form-control" id="db_user" name="db_user" required>
        </div>
        <div class="form-group">
            <label for="db_pass">数据库密码</label>
            <input type="password" class="form-control" id="db_pass" name="db_pass">
        </div>
        <div class="form-group">
            <label for="site_title">网站名称</label>
            <input type="text" class="form-control" id="site_title" name="site_title" required>
        </div>
        <button type="submit" class="btn btn-primary">安装</button>
    </form>
</div>
<script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
<script src="https://cdn.bootcdn.net/ajax/libs/twitter-bootstrap/4.5.2/js/bootstrap.bundle.min.js"></script>
</body>
</html>

create_paste.php

代码语言:javascript
复制
<?php
require 'database.php';

$content = $_POST['content'];
$password = $_POST['password'] ? password_hash($_POST['password'], PASSWORD_DEFAULT) : null;
$expiration_minutes = ($_POST['expire_condition'] == 'time') ? $_POST['expiration_minutes'] : null;
$max_views = ($_POST['expire_condition'] == 'views') ? $_POST['max_views'] : null;
$identifier = bin2hex(random_bytes(8)); // 生成唯一标识符

// 后端输入验证
if ($_POST['expire_condition'] === 'time' && empty($expiration_minutes)) {
    $response = ['success' => false, 'message' => '请设置销毁时间。'];
    header('Content-Type: application/json');
    echo json_encode($response);
    exit;
}

if ($_POST['expire_condition'] === 'views' && empty($max_views)) {
    $response = ['success' => false, 'message' => '请设置最大访问次数。'];
    header('Content-Type: application/json');
    echo json_encode($response);
    exit;
}

try {
    $db = connect();
    $stmt = $db->prepare("INSERT INTO pastes (content, password, expiration_minutes, max_views, identifier) VALUES (?, ?, ?, ?, ?)");
    $stmt->execute([$content, $password, $expiration_minutes, $max_views, $identifier]);

    // 构建完整的 URL
    $baseUrl = (isset($_SERVER['HTTPS']) ? 'https://' : 'http://') . $_SERVER['HTTP_HOST'];
    $link = $baseUrl . '/view.php?id=' . $identifier;

    $response = [
        'success' => true,
        'link' => $link,  // 返回完整的链接
        'expiration_minutes' => $expiration_minutes,
        'max_views' => $max_views,
        'password_set' => $password ? $_POST['password'] : null // 直接返回明文密码
    ];
    
} catch (PDOException $e) {
    $response = ['success' => false, 'message' => '保存<?php echo SITE_TITLE; ?>时发生错误: ' . $e->getMessage()];
}

header('Content-Type: application/json');
echo json_encode($response);

view.php

代码语言:javascript
复制
<?php
require 'config.php';
require 'database.php';

// 开启错误报告
ini_set('display_errors', 1);
ini_set('display_startup_errors', 1);
error_reporting(E_ALL);

if (isset($_GET['id']) && !empty($_GET['id'])) {
    $identifier = $_GET['id'];
} else {
    echo '缺少必要的 ID 参数。';
    exit;
}

try {
    $db = connect();
    $stmt = $db->prepare("
        SELECT content, password, expiration_minutes, max_views, current_views, created_at
        FROM pastes
        WHERE identifier = :identifier
    ");
    $stmt->execute([':identifier' => $identifier]);
    $paste = $stmt->fetch();

    if ($paste) {
        $createdTime = new DateTime($paste['created_at']);
        $currentTime = new DateTime('now');

        // 处理过期时间
        if ($paste['expiration_minutes'] !== null) {
            $createdTime->modify("+{$paste['expiration_minutes']} minutes");
            if ($currentTime > $createdTime) {
                echo '该内容已过期。';
                $db->prepare("DELETE FROM pastes WHERE identifier = :identifier")->execute([':identifier' => $identifier]);
                exit;
            }
        }

        // 处理最大访问次数
        if ($paste['max_views'] !== null && $paste['current_views'] >= $paste['max_views']) {
            echo '该内容已达到最大访问次数。';
            $db->prepare("DELETE FROM pastes WHERE identifier = :identifier")->execute([':identifier' => $identifier]);
            exit;
        }

        // 处理密码
        if ($paste['password']) {
            if ($_SERVER['REQUEST_METHOD'] == 'POST') {
                $entered_password = $_POST['password'];
                if (!password_verify($entered_password, $paste['password'])) {
                    echo '密码不正确。';
                    exit;
                }
            } else {
                // 显示密码输入表单
                ?>
                <!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <link href="https://cdn.staticfile.net/twitter-bootstrap/4.6.1/css/bootstrap.min.css" rel="stylesheet">
    <title><?php echo SITE_TITLE; ?> - 内容查看</title>
</head>
<body>
    <div class="container mt-5">
        <div class="card">
            <div class="card-header">请输入密码访问该内容</div>
            <div class="card-body">
                <form method="post">
                    <div class="form-group">
                        <label for="password">密码</label>
                        <input type="password" class="form-control" id="password" name="password" required>
                    </div>
                    <button type="submit" class="btn btn-primary">提交</button>
                </form>
            </div>
        </div>
    </div>
</body>
</html>
                <?php
                exit;
            }
        }

        // 更新查看次数
        $db->prepare("UPDATE pastes SET current_views = current_views + 1 WHERE identifier = :identifier")->execute([':identifier' => $identifier]);

        // 显示内容
        ?>
        <!DOCTYPE html>
        <html lang="zh-CN">
        <head>
            <meta charset="UTF-8">
            <title><?php echo SITE_TITLE; ?> - 查看内容</title>
            <link href="https://cdn.staticfile.net/twitter-bootstrap/4.6.1/css/bootstrap.min.css" rel="stylesheet">
        </head>
        <body>
        <div class="container mt-5">
            <div class="card">
                <div class="card-header">查看内容</div>
                <div class="card-body">
                    <pre><?php echo htmlspecialchars($paste['content']); ?></pre>
                </div>
            </div>
        </div>
        </body>
        </html>
        <?php

    } else {
        echo '未找到该内容的记录。';
    }
} catch (PDOException $e) {
    echo '查看失败: ' . $e->getMessage();
}
?>

database.php

代码语言:javascript
复制
<?php
require 'config.php';

function connect() {
    $dsn = "mysql:host=" . DB_HOST . ";dbname=" . DB_NAME . ";charset=utf8mb4";
    try {
        return new PDO($dsn, DB_USER, DB_PASS, [
            PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
            PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
        ]);
    } catch (PDOException $e) {
        throw new RuntimeException('数据库连接错误: ' . $e->getMessage());
    }
}
?>

本文系转载,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文系转载前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 1. 平台简介
  • 2. 前端页面分析
    • 2.1 页面结构
      • 2.2 交互逻辑
      • 3. 后端逻辑分析
        • 3.1 数据库保存逻辑
          • 3.2 销毁逻辑
          • 4. 内容查看逻辑
            • 4.1 验证和访问控制
              • 4.2 内容显示
              • 5. 总结
              • 6. 成品展示
              • 7. 完整代码
              相关产品与服务
              云数据库 MySQL
              腾讯云数据库 MySQL(TencentDB for MySQL)为用户提供安全可靠,性能卓越、易于维护的企业级云数据库服务。其具备6大企业级特性,包括企业级定制内核、企业级高可用、企业级高可靠、企业级安全、企业级扩展以及企业级智能运维。通过使用腾讯云数据库 MySQL,可实现分钟级别的数据库部署、弹性扩展以及全自动化的运维管理,不仅经济实惠,而且稳定可靠,易于运维。
              领券
              问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档