You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

求助:如何将带user_id的Arduino数据写入数据库及项目功能实现

实现Arduino数据附带user_id写入数据库(含登录验证与单用户独占)

嘿,结合你的场景,我帮你梳理一套完整的实现方案,一步步来搞定这个需求:

一、先把登录用户的身份传递给iframe里的Arduino页面

首先得让嵌入的Arduino页面能拿到当前登录用户的user_id,分两种情况处理:

情况1:iframe和主页面同域

这种最简单,因为同域下Session是共享的,直接在Arduino页面里读取Session即可:

// Arduino项目页面的开头,必须先启动Session
session_start();

// 先验证用户是否登录
if (!isset($_SESSION['log_user'])) {
    // 未登录就跳转到登录页或者提示无权限
    header("Location: login.php");
    exit;
}

// 根据Session里的用户名,从用户表获取对应的user_id
// 假设你的用户表是`users`,字段为`user_id`(主键)和`username`
$user_name = $_SESSION['log_user'];
$stmt = mysqli_prepare($conn, "SELECT user_id FROM users WHERE username = ?");
mysqli_stmt_bind_param($stmt, "s", $user_name);
mysqli_stmt_execute($stmt);
$result = mysqli_stmt_get_result($stmt);
$user = mysqli_fetch_assoc($result);
$user_id = $user['user_id'];

情况2:iframe和主页面跨域

如果是跨域,就得用postMessage安全传递身份信息:

  • 主页面(登录后的页面):等iframe加载完成后,发送带user_id的消息:
// 给iframe绑定加载完成事件
document.getElementById('your-arduino-iframe').addEventListener('load', function() {
    // 只给指定域名的iframe发消息,防止泄露信息
    this.contentWindow.postMessage(
        { type: 'user_auth', user_id: '<?php echo $user_id; ?>' },
        'https://your-arduino-page-domain.com' // 替换成实际的iframe域名
    );
});
  • Arduino页面:接收消息并验证来源,把user_id存到隐藏字段:
window.addEventListener('message', function(event) {
    // 严格验证发送方域名,拒绝非法消息
    if (event.origin !== 'https://your-main-site-domain.com') return;
    
    if (event.data.type === 'user_auth') {
        // 把user_id存入隐藏输入框,方便后续提交数据用
        document.getElementById('user_id_hidden').value = event.data.user_id;
    }
});
<!-- Arduino页面里加个隐藏字段 -->
<input type="hidden" id="user_id_hidden" name="user_id">

二、实现同一时间仅允许一名用户使用Arduino页面

需要给用户加个「占用状态」标记,防止多人同时操作:

1. 数据库准备

在users表新增两个字段:

  • is_using_arduino:TINYINT(1),默认0(0=未占用,1=占用)
  • last_active:DATETIME,记录用户最后活跃时间(处理页面崩溃未释放的情况)

2. 进入页面时验证占用状态

在Arduino页面的开头,获取user_id后添加验证逻辑:

// 检查当前是否有其他用户占用设备
$stmt = mysqli_prepare($conn, "SELECT user_id FROM users WHERE is_using_arduino = 1");
mysqli_stmt_execute($stmt);
$result = mysqli_stmt_get_result($stmt);

if (mysqli_num_rows($result) > 0) {
    $occupied_user = mysqli_fetch_assoc($result);
    // 如果占用的不是当前用户,直接提示并退出
    if ($occupied_user['user_id'] != $user_id) {
        echo "抱歉,当前设备正在被其他用户使用,请稍后再试!";
        exit;
    }
}

// 标记当前用户为占用状态,并更新活跃时间
$now = date('Y-m-d H:i:s');
$stmt = mysqli_prepare($conn, "UPDATE users SET is_using_arduino = 1, last_active = ? WHERE user_id = ?");
mysqli_stmt_bind_param($stmt, "si", $now, $user_id);
mysqli_stmt_execute($stmt);

3. 离开页面时释放占用状态

用JS监听页面关闭/刷新事件,发送请求释放状态:

window.addEventListener('beforeunload', function() {
    // 用AJAX发送释放请求
    fetch('release-arduino.php', {
        method: 'POST',
        headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
        body: `user_id=${document.getElementById('user_id_hidden').value}`
    });
});

对应的release-arduino.php:

session_start();
$user_id = $_POST['user_id'];

// 验证用户确实是占用者
$stmt = mysqli_prepare($conn, "SELECT is_using_arduino FROM users WHERE user_id = ?");
mysqli_stmt_bind_param($stmt, "i", $user_id);
mysqli_stmt_execute($stmt);
$result = mysqli_stmt_get_result($stmt);
$user = mysqli_fetch_assoc($result);

if ($user && $user['is_using_arduino'] == 1) {
    // 释放占用状态
    $stmt = mysqli_prepare($conn, "UPDATE users SET is_using_arduino = 0 WHERE user_id = ?");
    mysqli_stmt_bind_param($stmt, "i", $user_id);
    mysqli_stmt_execute($stmt);
}

4. 超时自动释放(兜底方案)

如果用户页面崩溃没触发beforeunload,需要定时清理超时的占用状态:

  • 心跳机制:Arduino页面每隔30秒发送一次心跳,更新活跃时间:
setInterval(function() {
    fetch('heartbeat.php', {
        method: 'POST',
        headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
        body: `user_id=${document.getElementById('user_id_hidden').value}`
    });
}, 30000); // 30秒一次

heartbeat.php代码:

$user_id = $_POST['user_id'];
$now = date('Y-m-d H:i:s');
$stmt = mysqli_prepare($conn, "UPDATE users SET last_active = ? WHERE user_id = ?");
mysqli_stmt_bind_param($stmt, "si", $now, $user_id);
mysqli_stmt_execute($stmt);
  • 定时任务:用服务器的Cron(Linux)或任务计划(Windows),每隔1分钟执行一次SQL,释放5分钟未活跃的占用:
UPDATE users SET is_using_arduino = 0 
WHERE is_using_arduino = 1 
AND last_active < DATE_SUB(NOW(), INTERVAL 5 MINUTE);

三、将Arduino数据附带user_id写入数据库

不管是表单提交还是AJAX发送数据,都要把user_id和Arduino数据一起传给后端:

示例:表单提交

<!-- Arduino页面的表单 -->
<form action="save-arduino-data.php" method="POST">
    <input type="hidden" name="user_id" value="<?php echo $user_id; ?>"> <!-- 同域直接输出,跨域用JS设置 -->
    <input type="text" name="sensor_value" placeholder="传感器数据">
    <!-- 其他Arduino相关字段 -->
    <button type="submit">提交数据</button>
</form>

后端保存逻辑(save-arduino-data.php)

session_start();
$user_id = $_POST['user_id'];
$sensor_value = $_POST['sensor_value'];

// 先验证用户权限:必须登录且是当前占用者
$stmt = mysqli_prepare($conn, "SELECT is_using_arduino FROM users WHERE user_id = ?");
mysqli_stmt_bind_param($stmt, "i", $user_id);
mysqli_stmt_execute($stmt);
$result = mysqli_stmt_get_result($stmt);
$user = mysqli_fetch_assoc($result);

if (!$user || $user['is_using_arduino'] != 1) {
    echo "无权限提交数据,请先登录并获取设备使用权限!";
    exit;
}

// 插入数据到Arduino数据表(假设表名为`arduino_data`)
$stmt = mysqli_prepare($conn, "INSERT INTO arduino_data (user_id, sensor_value, created_at) VALUES (?, ?, NOW())");
mysqli_stmt_bind_param($stmt, "is", $user_id, $sensor_value);

if (mysqli_stmt_execute($stmt)) {
    echo "数据保存成功!";
} else {
    echo "保存失败:" . mysqli_error($conn);
}

四、关键安全提醒

  • 始终使用预处理语句:避免SQL注入,别直接拼接SQL语句。
  • 严格权限验证:每一步操作都要验证用户是否登录、是否是当前占用者,防止非法提交。
  • 跨域消息验证:如果是跨域iframe,一定要验证postMessage的来源域名,防止恶意攻击。
  • Session过期处理:如果用户Session过期,要自动释放设备占用状态,可以在验证Session时同步处理。

内容的提问来源于stack exchange,提问作者Michal Koníček

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.26 08:41:12