重要的
在遵循以下答案中的建议后,客户能够毫无问题地登录,但并未尝试实际导航受保护的页面。当他稍后尝试这样做时,他又像以前一样重新登录,并出现“请登录”错误。经过一番摸索,我想到了一些非常简单的事情 - 客户端正在使用http://www.example.com/admin访问该站点,并且登录脚本中的所有内容都重定向到http://example.com,因此会话它正在寻找的 cookie 是为另一个域设置的。这也解释了为什么他在第一次登录时遇到问题,但后来却没有——脚本将他重定向到没有 www 的登录表单。
一个快速的解决方法是编写一个 .htaccess 文件来删除 www,问题解决了。当然,这也可以在登录脚本中处理,我将对其进行改进以备将来使用。
原帖
我使用自制的 CMS 和登录系统开发基于 PHP 和 MySQL 的站点。我的 CMS 对每个客户来说都是独一无二的,而且很受大众欢迎 - 不幸的是,我的登录系统并非如此。以下是一篇很长的文章,但我需要介绍详细信息以尝试找到解决方案。忍受我..
该系统相当简单,即使不是有点沉重。每个用户在 SALT 旁边都有一个存储在 MySQL 表中的盐渍哈希。当用户登录时,他们的 SALT 被检索并且提交的密码变成了一个加盐哈希。
如果提交的加盐哈希与存储在表中的哈希匹配,则用户通过身份验证。他们的姓名、最后一个 IP 地址和帐户级别(大多数站点上为 3 个级别)等详细信息存储在分配给会话变量的数组中。然后他们被重定向到他们登录的受限站点的登录页面(仅限会员或管理员/CMS)。
安全页面包括一个较小的 auth.php 文件,该文件检查是否存在包含其详细信息的会话变量。如果没有,他们将被重定向到该站点的登录表单,并显示一条错误消息,内容为“请登录”。如果存在,则允许它们继续,并将存储在数组中的详细信息分配给变量。
许多用户反映的问题是,他们经常需要多次登录,以免被弹回登录表单并显示“请登录”错误消息,或者他们导航到安全中的另一个页面网站并随机返回登录并出现相同的错误。因此,会话变量似乎要么没有被设置,要么在正常使用站点期间由于某种原因被清除。
第一个问题从未发生在我身上——在众多设备和网络上——我在客户办公室使用他们的笔记本电脑亲眼目睹了它。我让他们连接到我的移动热点,但没有任何变化。但是,他们能够使用我的笔记本电脑和热点连接毫无问题地登录。不幸的是,我无法使用我的笔记本电脑连接到他们的网络,因此不能排除这个变量。
*注意 - *我最初忘记提到,在问题客户使用正确的凭据登录两到三次后,系统可以正常工作。在他们的浏览器保持打开状态的情况下进行的后续登录尝试往往会在此后毫无问题地执行。此外,登录页面会破坏会话。
以下是每个阶段的代码,从登录脚本开始:
登录.php
<?php
putenv("TZ=US/Eastern");
if (array_key_exists('site', $_POST)) {
$authenticate = new loginUser($_POST['username'], $_POST['password'], $_POST['site'], $_SERVER['REMOTE_ADDR']);
}
//Authenticate and log-in
class loginUser {
private $memDB, $username, $password, $site, $ip_address;
//Clean input variables
private function clean($str) {
$str = @trim($str);
if(get_magic_quotes_gpc()) {
$str = stripslashes($str);
}
return $str;
}
//Construct variables
function __construct($username, $password, $site, $ip_address) {
session_start();
$this->memDB = new PDO('mysql:host=localhost;dbname=exampleDB', 'exampleUser', 'examplePassword');
$this->username = $this->clean($username);
$this->password = $this->clean($password);
$this->site = $site;
$this->ip_address = $ip_address;
$this->authUser();
}
//Validate username
private function validateUsername($username) {
$checkUsername = $this->memDB->prepare("SELECT COUNT(*) FROM accounts WHERE username = ?");
$checkUsername->execute(array($username));
return $checkUsername->fetchColumn();
}
//Obtain and set account details
private function accountDetails() {
$fetchAccountDetails = $this->memDB->prepare("SELECT id, name_f, name_l, ipAddr, lastLogin, accountLevel, isActive
FROM accounts WHERE username = ?");
$fetchAccountDetails->execute(array($this->username));
$accountDetails = $fetchAccountDetails->fetch();
$this->updateLogin();
return $accountDetails;
}
//Update last login details
private function updateLogin() {
$updateLogin = $this->memDB->prepare("UPDATE accounts SET ipAddr = ?, lastLogin = DATE_ADD(NOW(), INTERVAL 1 HOUR) WHERE username = ?");
$updateLogin->execute(array($this->ip_address, $this->username));
}
public function authUser() {
$loginErr = array(); //Array for holding login error message
$loginErrFlag = false; //Boolean for error
//Validate submitted $_POST elements
if (!$this->username) {
$loginErr[] = "Username missing";
$loginErrFlag = true;
}
if (!$this->password) {
$loginErr[] = "Password missing";
$loginErrFlag = true;
}
if ($this->username && $this->validateUsername($this->username) == 0) {
$loginErr[] = "Username invalid";
$loginErrFlag = true;
}
if (!$loginErrFlag) {
//Fetch the password and SALT to compare to entered password
$validatePW = $this->memDB->prepare("SELECT password, salt FROM accounts WHERE username = ? LIMIT 1");
$validatePW->execute(array($this->username));
$passwordResult = $validatePW->fetch();
$dbPW = $passwordResult['password'];
$dbSalt = $passwordResult['salt'];
//Compare entered password to SALT + hash
$hashPW = hash('sha512', $dbSalt . $this->password);
if ($hashPW === $dbPW) {
//Logged in
$_SESSION['CVFD-USER-DETAILS'] = $this->accountDetails();
//Redirect to secure landing page for log-in origin (Members or Admin)
//Adding SID is a recent attempt to handle log-in problems
header("Location: http://example.com/$this->site/$this->site-main.php?" . SID);
//session_write_close() was here but was removed
exit();
} else {
//Password invalid
$loginErr[] = "Please check your password and try again";
$_SESSION['CVFD_LOGIN_ERR'] = $loginErr;
//Redirect to the log-in for the origin
header("Location: http://example.com/$this->site");
session_write_close();
exit();
}
} else {
$_SESSION['CVFD_LOGIN_ERR'] = $loginErr;
header("Location: http://example.com/$this->site");
session_write_close();
exit();
}
}
}
?>
授权文件
<?php
session_start();
if (!isset($_SESSION['CVFD-USER-DETAILS']) || $_SESSION['CVFD-USER-DETAILS'] == '') {
//Not logged in
$_SESSION['CVFD_LOGIN_ERR'] = array('Please login');
header('Location: http://example.com/members');
session_write_close();
exit();
} else {
$userDetails = $_SESSION['CVFD-USER-DETAILS']; //Assign user details array to variable
//Check to see if account is active
$accountStatus = $userDetails['isActive'];
$accountLevel = $userDetails['accountLevel'];
if ($accountStatus == 0) {
//Account is not yet active (pending Admin activation)
$_SESSION['CVFD_LOGIN_ERR'] = array('Your account is suspended or pending activation');
header('Location: http://example.com/members');
session_write_close();
exit();
} else {
$CVFDFirstName = $userDetails['name_f'];
$CVFDLastName = $userDetails['name_l'];
$CVFDLastLogin = date("m/d/Y H:i:s", strtotime($userDetails['lastLogin']));
$CVFDAccountLevel = $userDetails['accountLevel'];
$CVFDIPAddr = $userDetails['ipAddr'];
}
}
?>
以下是 auth.php 包含在安全文件中的方式 -
<?php
if (substr_count($_SERVER['HTTP_ACCEPT_ENCODING'], 'gzip')) ob_start("ob_gzhandler"); else ob_start();
require($_SERVER['DOCUMENT_ROOT'] . '/members/includes/handlers/handler.auth.php');
任何帮助,将不胜感激。真是个谜。。
谢谢!