如何让按钮执行 php 脚本?
How to make a button execute a php script?
如何让 html 按钮执行 php 脚本?
我正在开发一个只有一个用户可以登录的登录系统,每当其他用户尝试登录时,它应该给他们一个警告弹出消息说 another user is already logged in - Do you want to take over from them?
- 如果
userA
已经登录并且 userB
尝试登录那么它应该在弹出窗口中显示为 userA is already logged in, do you want to take over?
.
- 现在
userA
在幕后,我们将取消写入权限并向他们显示另一条弹出消息,表明您的写入权限已被撤销。
第二点我可以稍后处理,但目前我专注于第一点。下面是我的代码,我试图在 LINE A
-
处实现第一点
if (isset($_POST['user_name'], $_POST['user_pass']) && $_POST['user_login'] == 1) {
//Assigning posted values to variables.
$username = $_POST['user_name'];
$password = $_POST['user_pass'];
//Checking the values are existing in the database or not
$stmt = $connect->prepare("SELECT user_pass FROM userspanel WHERE user_name=?");
$stmt->bind_param('s', $username);
$stmt->execute();
$result = $stmt->get_result();
$user_from_db = $result->fetch_object();
if ($user_from_db && password_verify($password, $user_from_db->user_pass)) {
$_SESSION['user_name'] = $username;
$sql = "SELECT * FROM trace_users where open='true'";
$result1 = mysqli_query($connect, $sql);
if($result1 = mysqli_query($connect, $sql)){
if(mysqli_num_rows($result1) > 0){
while($row = mysqli_fetch_array($result1)){
if($row['open'] == "true") {
if(!isset($_SESSION['pageadmin'])) {
$message = "user " . $row['user_name'] . " is logged in. Do you want to take over ?";
// LINE A
// how to show pop up button which shows $message on it
// and add action on ok and cancel button here?
}
break;
}
}
} else{
$_SESSION['pageadmin'] = true;
$open = "true";
$read_access = "1";
$write_access = "1";
$stmt = $connect->prepare("UPDATE trace_users SET open=?, read_access=?, write_access=? WHERE user_name=?");
$stmt->bind_param('ssss', $open, $read_access, $write_access, $username);
$stmt->execute();
}
}
} else {
echo "Invalid username and password";
}
}
我想完成以下任务,但我对如何实现感到困惑 -
- 在
LINE A
,我想显示一条弹出消息说 "Previous user is logged in. Do you want to take over?"
,带有确定和取消按钮。
- 单击弹出消息上的
Ok
按钮后,我想在 php 中执行某些代码,假设现在打印 hello world
,如果我单击 cancel
按钮,然后我不想做任何事情。
简而言之,我想实现两件事:
1. 在 A 行创建一个带有确定和取消按钮的弹出消息。
2. 单击“确定”按钮,我想执行 php 脚本,单击“取消”按钮,我不想发生任何事情。
这里是基本概念。
- 您需要一个中央存储,用于存储当前活动用户
active_user_id
和 last_updated
(database/file/memcache/redis 任何您想要的)
- 您需要为每个会话轮询当前活跃用户 - 如果当前用户不是活跃用户,他将注销并收到通知,这样他就知道其他人接管了。
流量
- 用户登录
$_SESSION['user_id'] = user_id
- IF:
$_SESSION['user_id']
!== active_user_id
(中央存储 - 见上文)AND last_updated
< 10s
- TRUE:登录,将
active_user_id
更新为 $_SESSION['user_id']
;更新 last_updated
到现在;重定向到 main-page
- FALSE:显示弹出窗口“接管?” -> ok: 与上面的 TRUE 相同,abort: close popup
- 虽然 logged-in 每 5 秒调用
check-active.php
check-active.php
(伪代码):
- IF:user_id 来自
$_SESSION['user_id']
=== active_user_id
来自 storage
- TRUE:在
storage
中更新 last_updated
; return 'ok';
- FALSE: 调用
session_destroy();
return 'failed';
frontend
伪代码(已登录):
- 每 5 秒调用
check-active.php
- 处理结果
- 如果:结果=== 'ok'
- 真:什么都不做
- FALSE:像 'main.php?message=' 一样重定向到主页。 urlencode('You got replaced')
Why check for last_updated
> 10s?
因为有人登录可以关闭浏览器,所以我们不知道会话是否仍然处于活动状态。所以我们检查 last_updated
(只要浏览器仍然打开,它就会每 5 秒更新一次)。如果 last_updated
> 10s,我们认为它是“当前没有人处于活动状态”,因此我们不必要求接管。
工作示例
打开 this - 点击“运行”(如果不是 运行ning)。
然后打开this两次。一个在隐身选项卡中,以获得第二个会话将自己踢出去。
免责声明:这绝对是实验性的。
这个排序答案是 Possible
。
您可以使用 JavaScript
和 jQuery
。
使用setInterval
userA和userB普通用户登录校验php
setInterval(function() {
// codes here
},5000); // for 5 seconds
点击功能后用户确认
$('#submit').on('click', function()
用confirm
调用$.ajax
函数
if(confirm("Are you sure ?")) {
// $.ajax codes here
}return false;
userB 尝试登录 setInterval
发送 20 秒等待响应。
userA ip 设置 20 秒后注销。
setInterval
userA 收到 "logout" 然后调用 logout php.
查看基本ip,user和logged信息php 与数据库。
我刚刚放置了用于此的哪个函数?不是完整的代码。
注意:下面的代码未经测试,但应该可以让您大致了解如何进行。
首先,您应该只撤销当前具有写入权限的人的写入权限,因此您的第一个 sql 应该是:
$sql = "SELECT * FROM trace_users where open='true' && write_access = '1'";
其次,由于您已经只选择了 'open' == "true"
的用户,因此再次检查 if($row['open'] == "true") {
是多余的。
第三,您应该将用户名放入会话中,以便以后操作和检索 logged-in 用户数据。
第四,您在下面的代码中连续两次执行相同的查询,所以不是:
if($result1 = mysqli_query($connect, $sql))
只需输入:
if($result1)
现在解决您的问题:
为了避免在确认后为第二次登录尝试来回传递名称和密码,我们可以使用令牌。
另外,那时我想保存用户名,因为我将在 A 行中使用它:
$_SESSION['login_token'] = sha1(time());
$_SESSION['user_name'] = $username;
而且我们必须有一种方法将该令牌返回给客户端,然后再返回给服务器,因此您可以在脚本的某处拥有:
if (isset($_SESSION['login_token']) && $_SESSION['login_token']) {
?>
<form id="confirm_form" action="login_script.php" method="POST">
<input type="hidden" name="login_token" value="<?= $_SESSION['login_token'] ?>">
</form>
<script>
// And the popup:
if (confirm('<?= addcslashes($message, "'/") ?>')) {
document.getElementById('confirm_form').submit();
}
</script>
<?php
}
我们还希望上面的 PHP 脚本处理会话登录,这也会断开其他用户的连接,因此我们将添加:
if (isset($_POST['login_token'], $_SESSION['login_token']) && $_POST['login_token'] === $_SESSION['login_token']) {
// block access to any write_access user
// I am going to make an assumption here - that your write_access column can take the value "-1"
// I am going to use it as a flag that means that this user needs to be told that his access has been revoked
mysqli_query($connect, "UPDATE trace_users SET write_access = '-1' where open='true'");
$_SESSION['pageadmin'] = true;
$_SESSION['login_token'] = null;
$open = "true";
$read_access = "1";
$write_access = "1";
$stmt = $connect->prepare("UPDATE trace_users SET open=?, read_access=?, write_access=? WHERE user_name=?");
$stmt->bind_param('ssss', $open, $read_access, $write_access, $username);
$stmt->execute();
}
最后我们需要一个前端脚本来检查权限的变化:
为此,我将使用轮询 + jQuery:
<script>
setInterval(function() {
$.get('check_permissions.php', function(data) {
if (data == '-1') {
alert('your write access is revoked');
}
});
}, 5000);
</script>
和check_permissions.php是:
$stmt = $connect->prepare("SELECT write_access FROM trace_users WHERE user_name = ?");
$stmt->bind_param('s', $_SESSION['user_name']);
$stmt->execute();
$stmt->bind_result($status);
$stmt->fetch();
echo $status;
if ($status == -1) {
// and update status of this user that he has no write permissions but no longer needs to be warned
$stmt = $connect->prepare("UPDATE trace_users SET write_access = 0 WHERE user_name = ?");
$stmt->bind_param('s', $_SESSION['user_name']);
$stmt->execute();
}
基本场景:
- 在
.txt
文件中以下列格式(如 JSON):
json_encode(['user_id' => 1, 'created_at' => (new DateTime('now'))->format('Y-m-d H:i:s')]);
- 当用户尝试登录时,检查文件中存储的
user_id
和 created_at
字段。
- 如果文件为空,则将用户登录并将用户的唯一标识和时间写入文件。
- 如果文件不是空并且
user_id
字段与用户的id
相同谁尝试登录并且 created_at
字段不早于 12 小时前(或您的自定义逻辑),只需更新文件中的 created_at
字段并让用户登录。
- 如果文件不是空并且
user_id
字段与用户的id
相同谁尝试登录,但超过 12 小时(或您的自定义逻辑),询问用户是否 he/she 想接管另一个用户。
- 如果文件 不是 空并且
user_id
字段是 不是 与尝试登录的用户的 id
相同 询问用户是否 he/she 想要接管另一个用户。
基本实现:
- 在您的项目目录中创建一个
.txt
文件。
- 将这些辅助函数添加到您的项目中(
helpers.php
在我的例子中):
if (! function_exists('check_auth')) {
function check_auth(): bool
{
if (! isset($_SESSION['user_id'])) {
return false;
}
if (! file_exists('current_user.txt') || filesize('current_user.txt') === 0) {
return true;
}
$trace = json_decode(file_get_contents('current_user.txt'));
// You can write your own logic here.
return (int) $trace->user_id === $_SESSION['user_id'] && (new DateTime($trace->created_at))->modify('+12 hours') > new Datetime('now');
}
}
if (! function_exists('logout'))
{
function logout()
{
if (isset($_SESSION['user_id'])) {
$trace = json_decode(file_get_contents('current_user.txt'));
if ((int) $trace->user_id === $_SESSION['user_id']) {
file_put_contents('current_user.txt', '');
}
unset($_SESSION['user_id']);
}
}
}
if (! function_exists('redirect')) {
function redirect(string $url, int $status_code = 303): void
{
header('Location: ' . $url, true, $status_code);
die();
}
}
- 创建一个登录页面(在我的例子中是
login.php
):
<?php
declare(strict_types=1);
// Start session.
session_start();
// Include helper functions.
require_once 'helpers.php';
// Redirect user to homepage/dashboard if authenticated.
if (check_auth()) {
redirect('index.php');
return;
}
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$pdo = new PDO('mysql:host=[DB_HOST];dbname=[DB_NAME];charset=utf8mb4', '[DB_USERNAME]', '[DB_PASSWORD]', [
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_OBJ,
PDO::ATTR_EMULATE_PREPARES => false,
]);
$stmt = $pdo->prepare('SELECT * FROM users WHERE email = ?');
$stmt->execute([$_POST['email']]);
$user = $stmt->fetch();
if (! ($user && password_verify($_POST['password'], $user->password))) {
echo json_encode([
'success' => false,
'message' => 'These credentials don\'t match our records.',
]);
return;
}
// Log user in if another is not authenticated.
if (filesize('current_user.txt') === 0) {
file_put_contents('current_user.txt', json_encode([
'user_id' => $user->id,
'created_at' => (new DateTime('now'))->format('Y-m-d H:i:s'),
]));
$_SESSION['user_id'] = $user->id;
echo json_encode([
'success' => true,
]);
return;
}
$trace = json_decode(file_get_contents('current_user.txt'));
// Log user in if the last authenticated user is himself/herself.
if ((int) $trace->user_id === $user->id) {
$trace->created_at = (new DateTime('now'))->format('Y-m-d H:i:s');
file_put_contents('current_user.txt', json_encode($trace));
$_SESSION['user_id'] = $user->id;
echo json_encode([
'success' => true,
]);
return;
}
// Ask user if he/she wants to take over.
echo json_encode([
'success' => false,
'takeover' => true,
'message' => 'Another user is logged in. Do you want to take over?',
]);
return;
}
?>
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport"
content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Login</title>
</head>
<body>
<form method="post" id="form">
<div>
<label for="email">Email:</label>
<input type="email" id="email" name="email" placeholder="Email" required>
</div>
<div>
<label for="password">Password:</label>
<input type="password" id="password" name="password" placeholder="Password">
</div>
<div>
<span id="message" style="color: red;"></span>
</div>
<button>Log in</button>
</form>
<script src="https://code.jquery.com/jquery-3.5.1.min.js"></script>
<script>
$(function () {
$('#form').submit(function (e) {
e.preventDefault();
$('#message').text('');
$.post('login.php', $(this).serialize(), function (response) {
const res = JSON.parse(response);
if (res.takeover) {
// Ask user if he/she wants to take over. If user confirms, run `confirmed()` function.
confirm(res.message) && confirmed();
return;
}
if (res.success) {
// Login is successful. Reload or redirect user to another page.
location.reload();
} else {
// Login failed. Incorrect email or password entered.
$('#message').text(res.message || '');
}
});
});
function confirmed() {
$.post('confirmed.php', function (response) {
const res = JSON.parse(response);
console.log(res.data);
});
}
});
</script>
</body>
</html>
- 检查是否有其他用户接管了您页面中当前经过身份验证的用户(
index.php
在我的例子中):
<?php
declare(strict_types=1);
// Start session.
session_start();
// Include helper functions.
require_once 'helpers.php';
?>
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport"
content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Home</title>
</head>
<body>
<?php if (check_auth()): ?>
Welcome friend.
<?php else: ?>
<a href="/login.php">Log in</a>
<?php endif; ?>
<script src="https://code.jquery.com/jquery-3.5.1.min.js"></script>
<script>
$(function () {
// Check if another user took over currently authenticated user in every 3 seconds.
setInterval(function () {
$.post('check.php', function (response) {
let res = JSON.parse(response);
if (! res.auth && res.redirect) {
location.replace(res.redirect);
}
});
}, 3000);
});
</script>
</body>
</html>
- 实施您的检查逻辑(在我的例子中是
check.php
):
<?php
declare(strict_types=1);
// Start session.
session_start();
// Include helper functions.
require_once 'helpers.php';
if (! check_auth()) {
logout();
echo json_encode([
'auth' => false,
'redirect' => 'login.php',
]);
return;
}
echo json_encode([
'auth' => true,
]);
- 最后,为用户确认接管的情况创建 action/function(在我的情况下为
confirmed.php
):
<?php
declare(strict_types=1);
// Start session.
session_start();
// Include helper functions.
require_once 'helpers.php';
/**
* Run your action here if user confirms to take over another user.
*/
echo json_encode([
'data' => 'User confirmed to take over another user.',
]);
已测试并且工作正常。但是您应该根据自己的需要对其进行自定义。
如何让 html 按钮执行 php 脚本?
我正在开发一个只有一个用户可以登录的登录系统,每当其他用户尝试登录时,它应该给他们一个警告弹出消息说 another user is already logged in - Do you want to take over from them?
- 如果
userA
已经登录并且userB
尝试登录那么它应该在弹出窗口中显示为userA is already logged in, do you want to take over?
. - 现在
userA
在幕后,我们将取消写入权限并向他们显示另一条弹出消息,表明您的写入权限已被撤销。
第二点我可以稍后处理,但目前我专注于第一点。下面是我的代码,我试图在 LINE A
-
if (isset($_POST['user_name'], $_POST['user_pass']) && $_POST['user_login'] == 1) {
//Assigning posted values to variables.
$username = $_POST['user_name'];
$password = $_POST['user_pass'];
//Checking the values are existing in the database or not
$stmt = $connect->prepare("SELECT user_pass FROM userspanel WHERE user_name=?");
$stmt->bind_param('s', $username);
$stmt->execute();
$result = $stmt->get_result();
$user_from_db = $result->fetch_object();
if ($user_from_db && password_verify($password, $user_from_db->user_pass)) {
$_SESSION['user_name'] = $username;
$sql = "SELECT * FROM trace_users where open='true'";
$result1 = mysqli_query($connect, $sql);
if($result1 = mysqli_query($connect, $sql)){
if(mysqli_num_rows($result1) > 0){
while($row = mysqli_fetch_array($result1)){
if($row['open'] == "true") {
if(!isset($_SESSION['pageadmin'])) {
$message = "user " . $row['user_name'] . " is logged in. Do you want to take over ?";
// LINE A
// how to show pop up button which shows $message on it
// and add action on ok and cancel button here?
}
break;
}
}
} else{
$_SESSION['pageadmin'] = true;
$open = "true";
$read_access = "1";
$write_access = "1";
$stmt = $connect->prepare("UPDATE trace_users SET open=?, read_access=?, write_access=? WHERE user_name=?");
$stmt->bind_param('ssss', $open, $read_access, $write_access, $username);
$stmt->execute();
}
}
} else {
echo "Invalid username and password";
}
}
我想完成以下任务,但我对如何实现感到困惑 -
- 在
LINE A
,我想显示一条弹出消息说"Previous user is logged in. Do you want to take over?"
,带有确定和取消按钮。 - 单击弹出消息上的
Ok
按钮后,我想在 php 中执行某些代码,假设现在打印hello world
,如果我单击cancel
按钮,然后我不想做任何事情。
简而言之,我想实现两件事:
1. 在 A 行创建一个带有确定和取消按钮的弹出消息。
2. 单击“确定”按钮,我想执行 php 脚本,单击“取消”按钮,我不想发生任何事情。
这里是基本概念。
- 您需要一个中央存储,用于存储当前活动用户
active_user_id
和last_updated
(database/file/memcache/redis 任何您想要的) - 您需要为每个会话轮询当前活跃用户 - 如果当前用户不是活跃用户,他将注销并收到通知,这样他就知道其他人接管了。
流量
- 用户登录
$_SESSION['user_id'] = user_id
- IF:
$_SESSION['user_id']
!==active_user_id
(中央存储 - 见上文)ANDlast_updated
< 10s- TRUE:登录,将
active_user_id
更新为$_SESSION['user_id']
;更新last_updated
到现在;重定向到 main-page - FALSE:显示弹出窗口“接管?” -> ok: 与上面的 TRUE 相同,abort: close popup
- TRUE:登录,将
- 虽然 logged-in 每 5 秒调用
check-active.php
check-active.php
(伪代码):
- IF:user_id 来自
$_SESSION['user_id']
===active_user_id
来自storage
- TRUE:在
storage
中更新last_updated
; return 'ok'; - FALSE: 调用
session_destroy();
return 'failed';
- TRUE:在
frontend
伪代码(已登录):
- 每 5 秒调用
check-active.php
- 处理结果
- 如果:结果=== 'ok'
- 真:什么都不做
- FALSE:像 'main.php?message=' 一样重定向到主页。 urlencode('You got replaced')
Why check for
last_updated
> 10s?
因为有人登录可以关闭浏览器,所以我们不知道会话是否仍然处于活动状态。所以我们检查 last_updated
(只要浏览器仍然打开,它就会每 5 秒更新一次)。如果 last_updated
> 10s,我们认为它是“当前没有人处于活动状态”,因此我们不必要求接管。
工作示例
打开 this - 点击“运行”(如果不是 运行ning)。
然后打开this两次。一个在隐身选项卡中,以获得第二个会话将自己踢出去。
免责声明:这绝对是实验性的。
这个排序答案是 Possible
。
您可以使用 JavaScript
和 jQuery
。
使用setInterval
userA和userB普通用户登录校验php
setInterval(function() {
// codes here
},5000); // for 5 seconds
点击功能后用户确认
$('#submit').on('click', function()
用confirm
调用$.ajax
函数
if(confirm("Are you sure ?")) {
// $.ajax codes here
}return false;
userB 尝试登录 setInterval
发送 20 秒等待响应。
userA ip 设置 20 秒后注销。
setInterval
userA 收到 "logout" 然后调用 logout php.
查看基本ip,user和logged信息php 与数据库。
我刚刚放置了用于此的哪个函数?不是完整的代码。
注意:下面的代码未经测试,但应该可以让您大致了解如何进行。
首先,您应该只撤销当前具有写入权限的人的写入权限,因此您的第一个 sql 应该是:
$sql = "SELECT * FROM trace_users where open='true' && write_access = '1'";
其次,由于您已经只选择了 'open' == "true"
的用户,因此再次检查 if($row['open'] == "true") {
是多余的。
第三,您应该将用户名放入会话中,以便以后操作和检索 logged-in 用户数据。
第四,您在下面的代码中连续两次执行相同的查询,所以不是:
if($result1 = mysqli_query($connect, $sql))
只需输入:
if($result1)
现在解决您的问题:
为了避免在确认后为第二次登录尝试来回传递名称和密码,我们可以使用令牌。 另外,那时我想保存用户名,因为我将在 A 行中使用它:
$_SESSION['login_token'] = sha1(time());
$_SESSION['user_name'] = $username;
而且我们必须有一种方法将该令牌返回给客户端,然后再返回给服务器,因此您可以在脚本的某处拥有:
if (isset($_SESSION['login_token']) && $_SESSION['login_token']) {
?>
<form id="confirm_form" action="login_script.php" method="POST">
<input type="hidden" name="login_token" value="<?= $_SESSION['login_token'] ?>">
</form>
<script>
// And the popup:
if (confirm('<?= addcslashes($message, "'/") ?>')) {
document.getElementById('confirm_form').submit();
}
</script>
<?php
}
我们还希望上面的 PHP 脚本处理会话登录,这也会断开其他用户的连接,因此我们将添加:
if (isset($_POST['login_token'], $_SESSION['login_token']) && $_POST['login_token'] === $_SESSION['login_token']) {
// block access to any write_access user
// I am going to make an assumption here - that your write_access column can take the value "-1"
// I am going to use it as a flag that means that this user needs to be told that his access has been revoked
mysqli_query($connect, "UPDATE trace_users SET write_access = '-1' where open='true'");
$_SESSION['pageadmin'] = true;
$_SESSION['login_token'] = null;
$open = "true";
$read_access = "1";
$write_access = "1";
$stmt = $connect->prepare("UPDATE trace_users SET open=?, read_access=?, write_access=? WHERE user_name=?");
$stmt->bind_param('ssss', $open, $read_access, $write_access, $username);
$stmt->execute();
}
最后我们需要一个前端脚本来检查权限的变化: 为此,我将使用轮询 + jQuery:
<script>
setInterval(function() {
$.get('check_permissions.php', function(data) {
if (data == '-1') {
alert('your write access is revoked');
}
});
}, 5000);
</script>
和check_permissions.php是:
$stmt = $connect->prepare("SELECT write_access FROM trace_users WHERE user_name = ?");
$stmt->bind_param('s', $_SESSION['user_name']);
$stmt->execute();
$stmt->bind_result($status);
$stmt->fetch();
echo $status;
if ($status == -1) {
// and update status of this user that he has no write permissions but no longer needs to be warned
$stmt = $connect->prepare("UPDATE trace_users SET write_access = 0 WHERE user_name = ?");
$stmt->bind_param('s', $_SESSION['user_name']);
$stmt->execute();
}
基本场景:
- 在
.txt
文件中以下列格式(如 JSON):
json_encode(['user_id' => 1, 'created_at' => (new DateTime('now'))->format('Y-m-d H:i:s')]);
- 当用户尝试登录时,检查文件中存储的
user_id
和created_at
字段。 - 如果文件为空,则将用户登录并将用户的唯一标识和时间写入文件。
- 如果文件不是空并且
user_id
字段与用户的id
相同谁尝试登录并且created_at
字段不早于 12 小时前(或您的自定义逻辑),只需更新文件中的created_at
字段并让用户登录。 - 如果文件不是空并且
user_id
字段与用户的id
相同谁尝试登录,但超过 12 小时(或您的自定义逻辑),询问用户是否 he/she 想接管另一个用户。 - 如果文件 不是 空并且
user_id
字段是 不是 与尝试登录的用户的id
相同 询问用户是否 he/she 想要接管另一个用户。
基本实现:
- 在您的项目目录中创建一个
.txt
文件。 - 将这些辅助函数添加到您的项目中(
helpers.php
在我的例子中):
if (! function_exists('check_auth')) {
function check_auth(): bool
{
if (! isset($_SESSION['user_id'])) {
return false;
}
if (! file_exists('current_user.txt') || filesize('current_user.txt') === 0) {
return true;
}
$trace = json_decode(file_get_contents('current_user.txt'));
// You can write your own logic here.
return (int) $trace->user_id === $_SESSION['user_id'] && (new DateTime($trace->created_at))->modify('+12 hours') > new Datetime('now');
}
}
if (! function_exists('logout'))
{
function logout()
{
if (isset($_SESSION['user_id'])) {
$trace = json_decode(file_get_contents('current_user.txt'));
if ((int) $trace->user_id === $_SESSION['user_id']) {
file_put_contents('current_user.txt', '');
}
unset($_SESSION['user_id']);
}
}
}
if (! function_exists('redirect')) {
function redirect(string $url, int $status_code = 303): void
{
header('Location: ' . $url, true, $status_code);
die();
}
}
- 创建一个登录页面(在我的例子中是
login.php
):
<?php
declare(strict_types=1);
// Start session.
session_start();
// Include helper functions.
require_once 'helpers.php';
// Redirect user to homepage/dashboard if authenticated.
if (check_auth()) {
redirect('index.php');
return;
}
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$pdo = new PDO('mysql:host=[DB_HOST];dbname=[DB_NAME];charset=utf8mb4', '[DB_USERNAME]', '[DB_PASSWORD]', [
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_OBJ,
PDO::ATTR_EMULATE_PREPARES => false,
]);
$stmt = $pdo->prepare('SELECT * FROM users WHERE email = ?');
$stmt->execute([$_POST['email']]);
$user = $stmt->fetch();
if (! ($user && password_verify($_POST['password'], $user->password))) {
echo json_encode([
'success' => false,
'message' => 'These credentials don\'t match our records.',
]);
return;
}
// Log user in if another is not authenticated.
if (filesize('current_user.txt') === 0) {
file_put_contents('current_user.txt', json_encode([
'user_id' => $user->id,
'created_at' => (new DateTime('now'))->format('Y-m-d H:i:s'),
]));
$_SESSION['user_id'] = $user->id;
echo json_encode([
'success' => true,
]);
return;
}
$trace = json_decode(file_get_contents('current_user.txt'));
// Log user in if the last authenticated user is himself/herself.
if ((int) $trace->user_id === $user->id) {
$trace->created_at = (new DateTime('now'))->format('Y-m-d H:i:s');
file_put_contents('current_user.txt', json_encode($trace));
$_SESSION['user_id'] = $user->id;
echo json_encode([
'success' => true,
]);
return;
}
// Ask user if he/she wants to take over.
echo json_encode([
'success' => false,
'takeover' => true,
'message' => 'Another user is logged in. Do you want to take over?',
]);
return;
}
?>
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport"
content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Login</title>
</head>
<body>
<form method="post" id="form">
<div>
<label for="email">Email:</label>
<input type="email" id="email" name="email" placeholder="Email" required>
</div>
<div>
<label for="password">Password:</label>
<input type="password" id="password" name="password" placeholder="Password">
</div>
<div>
<span id="message" style="color: red;"></span>
</div>
<button>Log in</button>
</form>
<script src="https://code.jquery.com/jquery-3.5.1.min.js"></script>
<script>
$(function () {
$('#form').submit(function (e) {
e.preventDefault();
$('#message').text('');
$.post('login.php', $(this).serialize(), function (response) {
const res = JSON.parse(response);
if (res.takeover) {
// Ask user if he/she wants to take over. If user confirms, run `confirmed()` function.
confirm(res.message) && confirmed();
return;
}
if (res.success) {
// Login is successful. Reload or redirect user to another page.
location.reload();
} else {
// Login failed. Incorrect email or password entered.
$('#message').text(res.message || '');
}
});
});
function confirmed() {
$.post('confirmed.php', function (response) {
const res = JSON.parse(response);
console.log(res.data);
});
}
});
</script>
</body>
</html>
- 检查是否有其他用户接管了您页面中当前经过身份验证的用户(
index.php
在我的例子中):
<?php
declare(strict_types=1);
// Start session.
session_start();
// Include helper functions.
require_once 'helpers.php';
?>
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport"
content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Home</title>
</head>
<body>
<?php if (check_auth()): ?>
Welcome friend.
<?php else: ?>
<a href="/login.php">Log in</a>
<?php endif; ?>
<script src="https://code.jquery.com/jquery-3.5.1.min.js"></script>
<script>
$(function () {
// Check if another user took over currently authenticated user in every 3 seconds.
setInterval(function () {
$.post('check.php', function (response) {
let res = JSON.parse(response);
if (! res.auth && res.redirect) {
location.replace(res.redirect);
}
});
}, 3000);
});
</script>
</body>
</html>
- 实施您的检查逻辑(在我的例子中是
check.php
):
<?php
declare(strict_types=1);
// Start session.
session_start();
// Include helper functions.
require_once 'helpers.php';
if (! check_auth()) {
logout();
echo json_encode([
'auth' => false,
'redirect' => 'login.php',
]);
return;
}
echo json_encode([
'auth' => true,
]);
- 最后,为用户确认接管的情况创建 action/function(在我的情况下为
confirmed.php
):
<?php
declare(strict_types=1);
// Start session.
session_start();
// Include helper functions.
require_once 'helpers.php';
/**
* Run your action here if user confirms to take over another user.
*/
echo json_encode([
'data' => 'User confirmed to take over another user.',
]);
已测试并且工作正常。但是您应该根据自己的需要对其进行自定义。