求助:如何将带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
相关产品推荐
相关产品推荐

