基于 PHP、MySQL 的登录系统问题 [英] Problems with PHP, MySQL based log-in system

查看:53
本文介绍了基于 PHP、MySQL 的登录系统问题的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

重要

按照以下答案中的建议进行操作后,客户端可以毫无问题地登录,但并未尝试实际导航受保护的页面.当他稍后尝试这样做时,他像以前一样返回登录,并显示请登录"错误.经过一番摸索,我想到了一些非常简单的事情 - 客户端正在使用 http://www.example 访问该站点.com/admin 并且登录脚本中的所有内容都重定向到 http://example.com,因此会话它正在寻找的 cookie 是为另一个域设置的.这也解释了为什么他第一次登录时出现问题,但随后没有问题 - 脚本将他重定向到没有 www 的登录表单.

After following the advice in the answers below, the client was able to log-in without any problems but did not attempt to actually navigate the secured pages. When he attempted to do so later, he was returned to log-in as before with the "Please log in" error. After much head scratching, something incredibly simple came to mind - the client was accessing the site with http://www.example.com/admin and everything in the login script was redirecting to http://example.com, so the session cookie that it was looking for was set for another domain. This also explains why he had problems logging in the first time but not subsequent times - the script redirected him to the log-in form without the www.

一个快速的解决方法是编写一个 .htaccess 文件来删除 www,问题解决了.当然,这也可以在登录脚本中处理,我会改进以备将来使用.

A quick fix was to write a .htaccess file to remove the www, problem solved. Of course this could also be handled within the login script, which I will improve for future use.

原帖

我使用自制的 CMS 和登录系统开发基于 PHP 和 MySQL 的站点.我的 CMS 对每个客户都是独一无二的,它一直很讨人喜欢 - 不幸的是,我的登录系统并非如此.以下是一篇很长的文章,但我需要涵盖细节以尝试找到解决方案.忍受我..

I develop PHP and MySQL based sites with a home brew CMS and log-in system. My CMS is unique to every client and it has been quite the crowd pleaser - unfortunately the same is not true of my log-in system. The following is a long post but I need to cover the details to try and find a solution. Bear with me..

该系统相当简单,即使不是有点笨重.每个用户都有一个加盐哈希存储在 MySQL 表中,与 SALT 一起.当用户登录时,他们的 SALT 被检索,提交的密码变成了一个加盐的哈希.

The system is fairly straight forward, if not a bit hefty. Each user has a salted hash stored in a MySQL table, alongside the SALT. When the user logs in, their SALT is retrieved and the submitted password becomes a salted hash.

如果提交的加盐哈希与存储在表中的哈希匹配,则用户通过身份验证.他们的姓名、最后一个 IP 地址和帐户级别(大多数站点上为 3 个级别)等详细信息存储在分配给会话变量的数组中.然后,他们将被重定向到他们登录的受限站点的登录页面(仅限会员或管理员/CMS).

If the submitted salted hash matches the one stored in the table, the user is authenticated. Details such as their name, last IP address, and account level (3 levels on most sites) are stored in an array assigned to a session variable. They are then redirected to the landing page of the restricted site to which they logged in (Members Only or Admin/CMS).

受保护的页面包含一个较小的 auth.php 文件,用于检查是否存在包含其详细信息的会话变量.如果没有,他们将被重定向到该站点的登录表单,并显示一条错误消息,内容为请登录".如果存在,则允许它们继续,并将存储在数组中的详细信息分配给变量.

Secured pages include a smaller auth.php file that checks to see if the session variable containing their details is present. If not, they are redirected to that site's log-in form with an error message that reads "Please log-in." If it is present, they are allowed to continue and the details stored in the array are assigned to variables.

许多用户反映的问题是,他们经常需要多次登录,以免被退回到带有请登录"错误消息的登录表单,或者他们导航到另一个页面在安全站点中,并随机返回登录并出现相同的错误.因此,会话变量似乎没有被设置,或者在站点的正常使用过程中由于某种原因被清除.

The problem that many users has reported is that they often need to log-in several times to keep from being bounced back to the log-in form with the "Please log-in" error message, or they navigate to another page in the secure site and randomly get bounced back to login with the same error. So, the session variable seems to either not be getting set or it is being cleared for some reason during normal use of the site.

第一个问题从来没有发生在我身上 - 通过大量设备和网络 - 我在客户办公室使用他们的笔记本电脑目睹了它.我让他们连接到我的移动热点,但没有任何变化.但是,他们可以使用我的笔记本电脑和我的热点连接毫无问题地登录.不幸的是,我无法使用笔记本电脑连接到他们的网络,因此不能排除该变量.

The first problem has NEVER happened to me - over a multitude of devices and networks - and I have witnessed it at a client's office using their laptop. I had them connect to my mobile hotspot and there was no change. However, they were able to log in without any problems using my laptop and my hotspot connection. Unfortunately, I was not able to connect to their network using my laptop, so that variable could not be ruled out.

*注意 - * 我忘了一开始我忘了提到,在问题客户使用正确的凭据登录两三次后,系统可以正常工作.当浏览器保持打开状态时,随后的登录尝试往往会在此后毫无问题地执行.此外,登录页面会破坏会话.

*NOTE - * I forgot to mention initially that the system works normally for problem clients after they have logged in two or three times with the correct credentials. Subsequent log-in attempts while their browser remains open tend to execute without problems thereafter. Also, the log-in page destroys the session.

这里是每个阶段的代码,从登录脚本开始:

Here is the code for each stage, starting with the log-in script:

login.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();
        }

    }
}
?>

auth.php

<?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 包含在安全文件中的方式-

Here is how the auth.php is included in secure files-

<?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');

任何帮助将不胜感激.真是个谜..

Any help would be appreciated. Quite a mystery..

谢谢!

推荐答案

让我印象深刻的一件事是:

The one thing that jumps out at me is the following:

header('Location: http://example.com/members');
session_write_close();
exit();

我会将 session_write_close() 调用放在 header('location ...')

您的日志中是否显示任何标头已发送"错误?

Are any 'headers already sent' errors showing up in your logs?

想到的其他事情是一些 AJAX 竞争条件.登录页面是否有异步调用?

Other thing that comes to mind is some AJAX race condition. Any async calls going on with login pages?

这篇关于基于 PHP、MySQL 的登录系统问题的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆