单击按钮 X 分钟不活动后如何注销?

How to logout after X minutes of inactivity on button click?

我正在做一个简单的项目,我有一个包含多个用户的登录系统,每个用户都将登录并将表单条目保存在一个文件中。我正在尝试建立会话超时,以便在 2 分钟不活动后它可以将我注销。

以上一切正常。现在我正在尝试构建此会话超时,以便它可以在 x 分钟不活动后将我注销并将我重定向到 login.php 页面。

这是我的 index.php 文件:

<?php

declare(strict_types = 1);

// Start session.
session_start();

// Include helper functions.
require_once 'helpers.php';

// 2 mins in seconds
$inactive = 120; 

if(isset($_SESSION['timeout']) ) {
    $session_life = time() - $_SESSION['timeout'];
    if($session_life > $inactive)
    { 
        redirect('logout.php');
        return;
    }
}

$_SESSION['timeout'] = time();

// Redirect user to login page if not authenticated.
if (! check_auth()) {
    redirect('login.php');
    return;
}

?>
<!doctype html>
<html>
<head>
    <title>Home</title>
</head>
<body>
    <div>
        <h1>Website Title</h1> <a href="logout.php">Logout</a> </div>
    <div>
        <p>Welcome back, <?= $_SESSION['user_id'] ?>!</p>
    </div>
    <form method="post">
        <input type="text" name="field1" />
        <input type="text" name="field2" />
        <input type="submit" name="submit" value="Save Data"> </form>
    <script src="https://code.jquery.com/jquery-3.5.1.min.js"></script>
    <script>
    $(function() {
        $('form').submit(function(e) {
            e.preventDefault();
            $.post({
                url: 'save.php',
                data: $(this).serialize(),
            }).done(response => {
                response = JSON.parse(response);
                if(response.message) {
                    alert(response.message);
                }
            });
        });
    });
    </script>
</body>
</html>

这是我的 save.php 文件:

<?php
declare(strict_types=1);
// Start session.
session_start();
// Include helper functions.
require_once 'helpers.php';

// 2 mins in seconds
$inactive = 120; 

if(isset($_SESSION['timeout']) ) {
    $session_life = time() - $_SESSION['timeout'];
    if($session_life > $inactive)
    { 
        redirect('logout.php');
        return;
    }
}

$_SESSION['timeout'] = time();

// Redirect user to login page if not authenticated.
if (! check_auth()) {
    redirect('login.php');
    return;
}

// save form entries in a file

这是我的 helpers.php 文件:

<?php
declare(strict_types=1);

if (! function_exists('check_auth')) {
    function check_auth(): bool
    {
        return isset($_SESSION['user_id']);
    }
}

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']) {
                $content = isset($trace->revoked_user_id)
                ? json_encode(['user_id' => $trace->revoked_user_id, 'created_at' => (new DateTime('now'))->format('Y-m-d H:i:s')])
                : '';
                file_put_contents('current_user.txt', $content);
            }

            unset($_SESSION['user_id']);
            unset($_SESSION['timeout']);
        }
    }
}

if (! function_exists('redirect')) {
    function redirect(string $url, int $status_code = 303): void
    {
        header('Location: ' . $url, true, $status_code);
        die();
    }
}

问题陈述

一旦 index.phpsave.php 空闲 2 分钟,我将重定向到 logout.php 文件。这是我注意到的 -

下面是我在 index.php 页面中的 ajax 调用 -

<script>
$(function() {
    $('form').submit(function(e) {
        e.preventDefault();
        $.post({
            url: 'save.php',
            data: $(this).serialize(),
        }).done(response => {
            response = JSON.parse(response);
            if(response.message) {
                alert(response.message);
            }
        });
    });
});
</script>

当我在 2 分钟不活动后单击 Save Data 按钮时,我的第 2 点会出现 json 解析错误。 response 变量的值在 html 中已满 login.php。我不确定为什么会这样。我们是否需要在 jquery 调用 save.php 之前检查会话验证?

总之,您的问题是由 ajax 请求时 save.php 中发生的重定向引起的。

发生的事情是 redirect() 请求被透明处理,结果由 jQuery.ajax().done() 闭包处理,它试图为 HTML 调用 JSON.parse(response); login.php。 单击按钮时,您应该能够通过在浏览器中查看开发人员工具(通常是 F12)来验证这一点。

解决此问题的一般方法是确定请求是否为 XMLHttpRequest 并发送不同的响应而不是重定向。

检测 XMLHttpRequest

要确定请求是否来自 jQuery.ajax(),您可以检查 X-Requested-With 请求 header。

isset($_SERVER['HTTP_X_REQUESTED_WITH']) && strtolower($_SERVER['HTTP_X_REQUESTED_WITH']) === 'xmlhttprequest'

JSON 响应 redirect 属性

这种方法 returns 在响应中使用 redirect 属性 并使用它从 jQuery.ajax().done() 闭包重定向。

helpers.php

if (! function_exists('redirect')) {
    function redirect(string $url, int $status_code = 303): void
    {
        if (isset($_SERVER['HTTP_X_REQUESTED_WITH']) && strtolower($_SERVER['HTTP_X_REQUESTED_WITH']) === 'xmlhttprequest') {
            //do not redirect AJAX requests
            echo json_encode(['redirect' => $url]);
        } else {
            header('Location: ' . $url, true, $status_code);
        }
        die();
    }
}

index.php

$(function() {
    "use strict";
    $('form').submit(function(e) {
        e.preventDefault();
        $.post({
            url: 'save.php',
            data: $(this).serialize(),
        }).done(response => {
            response = JSON.parse(response);
            if (response.redirect) {
                //redirect user
                window.location.href = response.redirect; 
            }
            if (response.message) {
                alert(response.message);
            }
        });
    });
});

状态代码响应

此方法 returns 一个不同于 200 Ok 的状态代码,而不是重定向并检查 jQuery.ajax()statusCode: 方法中的响应状态代码.

helpers.php

if (! function_exists('redirect')) {
    function redirect(string $url, int $status_code = 303): void
    {
        if (isset($_SERVER['HTTP_X_REQUESTED_WITH']) && strtolower($_SERVER['HTTP_X_REQUESTED_WITH']) === 'xmlhttprequest') {
            //do not redirect AJAX requests
            http_response_code(401); //Unauthorized - PHP 5.4+
            echo $url; //optionally output the url
        } else {
            header('Location: ' . $url, true, $status_code);
        }
        die();
    }
}

在这里您可以根据需要选择对状态码进行处理。 对于最简单的方法,检查 fail() jqXhr object 状态代码并改为执行 javascript 重定向。

index.php

$(function() {
    "use strict";
    $('form').submit(function(e) {
        e.preventDefault();
        $.post({
            url: 'save.php',
            data: $(this).serialize(),
        }).done(response => {
            response = JSON.parse(response);
            if (response.message) {
                alert(response.message);
            }
        }).fail(jqXhr => {
            if (jqXhr.status == 401) {
                //redirect to specified url in the response text
                window.location.href = jqXhr.responseText; 
               /* alternatively hard-code '/logout.php' */
            }
        });
    });
});