如果提供的变量超过必需的变量,则具有多个变量的清理 URL 将失败


Clean URLs with multiple variables failing if more than required variables are provided

我已经开始深入研究用户页面的干净URL,我的大部分灵感来自Stack Overflow的方式。用户可以只输入他们要查看的用户的用户 ID,然后这会将他们重定向到正确的 URL,包括显示名称。例如,如果我的帐户ID 为 1,则访问 users/1 将重定向到 users/1/JosephDuffy,假设我的显示名称是 JospehDuffy 。从这里,可以执行额外的操作,例如编辑自己,因此users/1/JosephDuffy/edit编辑用户 1。 users/1/username/edit也会重定向到 users/1/JosephDuffy/edit .上面所有的例子都在一个完美的世界中工作,但它很容易被打破。
用户可以对其他人执行操作,例如users/2/Player2/befriend 。显然,你不想和自己交朋友,所以users/1/JospehDuffy/befriend什么都不做。但是,输入 URL 如 users/1/1/1/1/JosephDuffy/befriend 似乎会触发操作befriend,显示名称为 JosephDuffy,用户 ID 为 1/1/1/1 。我猜这就是mod_rewrite的工作方式,但它似乎在扔给我一些曲线球。
目前,我在用户 id 上使用 intval(),这似乎有效但不太理想;我仍然留下多个提供相同信息的 URL,虽然我找不到任何其他"漏洞",但可能仍然有一些。我不确定问题出在哪里,所以我会发布我的.htaccess和PHP脚本。
.htaccess

RewriteEngine On
# Rewrite /users to provide the script with the GET variables it requires to function properly, whilst having clean URLs
# 3 variables were provided
RewriteRule ^users/([^.]+)/([^.]+)/([^.]+)/$ users.php?userid=$1&displayname=$2&action=$3
RewriteRule ^users/([^.]+)/([^.]+)/([^.]+)$ users.php?userid=$1&displayname=$2&action=$3
# 2 variables were provided
RewriteRule ^users/([^.]+)/([^.]+)/$ users.php?userid=$1&displayname=$2
RewriteRule ^users/([^.]+)/([^.]+)$ users.php?userid=$1&displayname=$2
#1 variable was provided
RewriteRule ^users/([^.]+)/$ users.php?userid=$1
RewriteRule ^users/([^.]+)$ users.php?userid=$1

我认为如果我在某个阶段使用 [R=301,L] 或类似的东西也会更好,但我不确定在哪里,或者为什么真的。
用户.php(有些部分被简化,几乎是psudocode,以使其更容易理解)

if (isset($_GET['userid'])) {
    $profileUserid = intval($_GET['userid']);
} else {
    $profileUserid = 0;
}
if (isset($_GET['displayname'])){
    $profileDisplayName = $_GET['displayname'];
} else {
    $profileDisplayName = '';
}
if (isset($_GET['action'])) {
    $action = $_GET['action'];
} else {
    $action = false;
}
$actualDisplayName = GetDisplayNameFromDBWhereid($profileUserid);
if ($actualDisplayName != $profileDisplayName) {
        header('Location: /users/' . $profileUserid . '/' . $actualDisplayName . '/' . $action);
        exit;
    } else if (substr($_SERVER['REQUEST_URI'], -1) != '/') {
        // There is no trailing slash, so add it
        header('Location: ' . $_SERVER['REQUEST_URI'] . '/');
        exit;
    }
    if ($currentUsersid == $profileUserid) {
        // Player is viewing themselves
        if ($action == 'edit') {
            echo 'Editing your profile';
        } else {
            // User viewing self, but not editing
            echo '<a href="edit">Edit your profile</a><br>';
        }
    } else {
        if ($action) {
            // Interact with the user whos profile is being viewed
        } else {
        }
    }

不使用intval($_GET['userid']),当我输入users/1/1/1/1/JosephDuffy/action时,它会action执行,就好像我在哪里是其他用户一样,而1/JosephDuffy/action显示"编辑您的个人资料"链接,忽略该操作,因为不等于"编辑"。
希望这是愚蠢的(我猜这是我写得不好的RewriteRules的错),但无论如何,感谢您的阅读。

为什么它会这样工作?

正则表达式是贪婪的,它们试图尽可能适合您的正则表达式,因此您的users/1/1/1/1/JosephDuffy/befriend非常适合模式users/([^.]+)/([^.]+)/([^.]+)。TBH 我不知道为什么[^.]+似乎和.+符号相同,这让我最想知道,但是......


如何修复

如果你想修复它,让它只适合users/1/JosephDuffy/befriend你需要把正则表达式模式作为

^users/([^/]+)/([^/]+)/([^/]+)$ users.php?userid=$1&displayname=$2&action=$3

并分别更改所有其余重写。请注意那里([^/]+)符号,这意味着所有字符,直到我们找到/


以及我会怎么做

如前所述,我仍然会考虑接受"任何类型的URL",然后在PHP端进行解析(使用preg_split)。所以URL重写将非常简单:

RewriteEngine On
RewriteCond %{SCRIPT_FILENAME} !-d
RewriteCond %{SCRIPT_FILENAME} !-f
RewriteRule ^(.*)$ users.php?url=$1 [QSA,L]

然后在代码中我会

$paramsAndValues = preg_split('#/#',$_GET['url']);

它很简单,但当然你会检查$_GET['url']是否存在等等。