用php实现用户登录 PHP客户端注册登录?前言自撰写本文以来的几年中,许多黑客攻击已将目标锁定在受密码保护的网站上客户密码的存储已成为许多讨论的主题,其中一些有用,而另一些则被误导了当然,没有人会以明文形式存储客户端密码,但是许多尝试的解决方案(消息摘要,加密等)都比明文略好可在此处找到有关密码存储的配套文章:PHP中的密码哈希以下讨论和文章部分“后记:关于存储密码”已过时,不应用作您的应用程序的基础介绍对于新PHP开发人员来说,常见的设计模式问题是这样的:“如何处理客户端注册和登录?” 它在每个框架和CMS中都已完成,并且它们都使用类似的模式本文逐步构建了模式,因此您可以查看代码各部分的内容对于此示例,我们依靠PHP会话处理程序来告诉我们客户端是否已登录我们还使用cookie,以便“记住”客户端已登录,并且我们使用了包含客户端的数据库表信息 我们对这种设计模式的实现使我们能够使用一行PHP代码对网页进行密码保护,如下所示:,今天小编就来说说关于用php实现用户登录 PHP客户端注册登录?下面更多详细答案一起来看看吧!
用php实现用户登录 PHP客户端注册登录前言自撰写本文以来的几年中,许多黑客攻击已将目标锁定在受密码保护的网站上。客户密码的存储已成为许多讨论的主题,其中一些有用,而另一些则被误导了。当然,没有人会以明文形式存储客户端密码,但是许多尝试的解决方案(消息摘要,加密等)都比明文略好。可在此处找到有关密码存储的配套文章:PHP中的密码哈希。以下讨论和文章部分“后记:关于存储密码”已过时,不应用作您的应用程序的基础。介绍对于新PHP开发人员来说,常见的设计模式问题是这样的:“如何处理客户端注册和登录?” 它在每个框架和CMS中都已完成,并且它们都使用类似的模式。本文逐步构建了模式,因此您可以查看代码各部分的内容。对于此示例,我们依靠PHP会话处理程序来告诉我们客户端是否已登录。我们还使用cookie,以便“记住”客户端已登录,并且我们使用了包含客户端的数据库表信息。 我们对这种设计模式的实现使我们能够使用一行PHP代码对网页进行密码保护,如下所示:
access_control();
此外,我们可以使用以下方法测试客户端登录(实际上不需要登录):
if (access_control(TRUE)) { }
本文结尾处有一些注释和后记。如果有时间,您可能需要向下滚动并立即阅读。当您尝试使用此设计时,它们会更深入地介绍您可能遇到的问题。然后回来并继续。约定和标准1.我们同意在所有页面上都使用session_start()的约定,没有例外。您可能希望将session_start()添加到通用脚本的顶部,该脚本会包含在所有页面脚本的顶部,以便您网站的每个页面都可以这样开始:
<?php require_once('RAY_EE_config.php'); // PHP AND HTML CODE FOLLOWS BELOW
“ config”脚本也是放置数据库连接和选择代码以及define()语句以及局部变量,类和函数的好地方,您可能已经知道要在一个数据库中包含这些公共元素。单一,易于查找的脚本。2.我们同意这样的约定:我们将access_control()语句添加到要保护的每个页面的非常顶部。为什么要顶?因为我们可能需要使用header()函数,并且这是HTTP的协议限制,所以必须在所有浏览器输出之前呈现并完成所有标头。header()语句将失败,并且如果违反此协议,则脚本将失败。在创建浏览器输出的同时,有多种方法可以遵循此协议。PHP函数ob_start()可以帮助您解决此问题。但是ob_start()对于这里的任务不是必需的-只有在完成所有标头命令之后,我们才能以正确的顺序处理数据并产生浏览器输出(如果有)。有了这些了解之后,让我们开始为访问控制系统构建框架。 通用元素-配置脚本第一步将是创建“配置”脚本,其中包含我们所有网页脚本所需的通用元素。您将需要对这些示例进行一些自定义。特别是,您将要在下面的脚本的第15-20行附近添加自己的数据库凭据。有了这些信息,您应该能够将这些代码片段中的每一个安装在您自己的服务器上,并运行它们以查看实际的“移动部件”。有了数据库凭证后,我们连接到数据库服务器并选择数据库(第21-34行)。看一看在第36行定义的access_control()函数。它所做的第一件事是保存客户端的入口点,以便我们在客户端认证之后可以返回到正确的页面。我们使用REQUEST_URI字符串而不是PHP_SELF,因为REQUEST_URI不仅包含我们的URL地址,还包含URL参数。PHP_SELF仅具有URL地址。接下来,我们测试会话UID变量。如果设置了此选项,则客户端已经登录,因此不需要进一步处理,并且我们返回TRUE。如果客户端未登录,则仅进入此函数的下一行。如果客户端未登录,则查看$ test参数。默认情况下,它设置为FALSE,但是如果调用脚本将其设置为TRUE,我们将返回一个指示信息,表明客户端未登录。如果客户端未登录,这不是测试,我们必须保护页面。因此,我们将客户端浏览器重定向到我们的登录页面并退出脚本。我们还需要一个函数来完成我们的工作-设置“记住我” cookie的函数。该定义从第53行开始。我们将cookie的“ uuk”命名为“唯一用户密钥”,并将其生存期取决于在第10行定义的常数REMEMBER的定义。有关setcookie()的更多信息,请参见PHP在线手册,位于:http : //php.net/manual/en/f unction.se tcookie.ph p有了“ config”框架,我们就可以通过执行从第80行开始的通用代码来开始每个网页。我们测试的第一件事是会话数组中是否存在“ uid”。如果有的话,我们就有一个经过身份验证的客户端,不需要其他处理。但是,如果未设置$ _SESSION [“ uid”],我们仍然可以通过“ uuk” cookie中的信息来记住该客户端。如果设置了cookie,则我们可以在数据库中查找客户端并自动完成登录,从而实现“记住我”的承诺。因为$ _cookie数组包含存储在客户端计算机上的数据,所以必须将其视为“污染的”外部数据。因此,最起码的检查是在查询中使用mysqli :: real_escape_string ()之前运行数据。使用这样准备的数据,我们可以查询用户表并尝试查找与该唯一用户密钥匹配的UID。如果我们在数据库中找到该UID,则将其复制到会话数组(第101行)中,并且客户端现在已登录。我们的最后一步是在第104行调用Remember_me()函数,从而扩展了该客户端的内存。通过在此调用此功能,可以使被记住并经常访问该站点的客户得到反复记住,甚至永远被记住。这是常见的“ config”脚本:
<?php // RAY_EE_config.php// WHEN WE ARE DEBUGGING OUR CODE, WE WANT TO SEE ALL THE ERRORS!error_reporting(E_ALL);// REQUIRED FOR PHP 5.1 date_default_timezone_set('America/Chicago');// THE LIFE OF THE "REMEMBER ME" cookiedefine('REMEMBER', 60*60*24*7); // ONE WEEK IN SECONDS// WE WANT TO START THE SESSION ON EVERY PAGEsession_start();// ConNECTION AND SELECtION VARIABLES FOR THE DATAbase$db_host = "localhost"; // PROBABLY THIS IS OK$db_name = "??"; // GET THESE FROM YOUR HOSTING COMPANY$db_user = "??";$db_word = "??";// OPEN A ConNECTION TO THE DATA base SERVER AND SELECt THE DB$mysqli = new mysqli($db_host, $db_user, $db_word, $db_name);// DID THE CONNECT/SELECT WORK OR FAIL?if ($mysqli->connect_errno){ $err = "ConNECT FAIL: " . $mysqli->connect_errno . ' ' . $mysqli->connect_error ; trigger_error($err, E_USER_ERROR);}// DEFINE THE ACCESS ConTROL FUNCTIonfunction access_control($test=FALSE){ // REMEMBER HOW WE GOT HERE $_SESSION["entry_uri"] = $_SERVER["REQUEST_URI"]; // IF THE UID IS SET, WE ARE LOGGED IN if (isset($_SESSION["uid"])) return $_SESSION["uid"]; // IF WE ARE NOT LOGGED IN - RESPOND TO THE TEST REQUEST if ($test) return FALSE; // IF THIS IS NOT A TEST, REDIRECT TO CALL FOR A LOGIN header("Location: RAY_EE_login.php"); exit;}// DEFINE THE "REMEMBER ME" cookie FUNCTIonfunction remember_me($uuk){ // ConSTRUCT A "REMEMBER ME" cookie WITH THE UNIQUE USER KEY $cookie_name = 'uuk'; $cookie_value = $uuk; $cookie_expires = time() date('Z') REMEMBER; $cookie_path = '/'; $cookie_domain = NULL; $cookie_secure = FALSE; $cookie_http = TRUE; // HIDE cookie FROM JAVAscript (PHP 5.2 ) // SEE http://php.net/manual/en/function.setcookie.php setcookie ( $cookie_name , $cookie_value , $cookie_expires , $cookie_path , $cookie_domain , $cookie_secure , $cookie_http ) ;}// DETERMINE IF THE CLIENT IS ALREADY LOGGED IN BECAUSE OF THE SESSION ARRAYif (!isset($_SESSION["uid"])){ // DETERMINE IF THE CLIENT IS ALREADY LOGGED IN BECAUSE OF "REMEMBER ME" FEATURE if (isset($_cookie["uuk"])) { $uuk = $mysqli->real_escape_string($_cookie["uuk"]); $sql = "SELECt uid FROM EE_userTable WHERe uuk = '$uuk' LIMIT 1"; $res = $mysqli->query($sql); // IF THE QUERY SUCCEEDED if ($res) { // THERE SHOULD BE ONE ROW $num = $res->num_rows; if ($num) { // RETRIEVE THE ROW FROM THE QUERY RESULTS SET $row = $res->fetch_assoc(); // STORE THE USER-ID IN THE SESSION ARRAY $_SESSION["uid"] = $row["uid"]; // EXTEND THE "REMEMBER ME" cookie remember_me($uuk); } } }}
数据库表-我们的客户数据模型现在,我们已经创建了常见的“ config”脚本,是时候创建将有助于访问控制功能的数据库表了。下面“创建”代码段中的这段代码应该做得很好。EE_userTable包含三列。这些是“ uid”列中的用户身份,“ pwd”列中的用户密码和“ uuk”列中的用户唯一键。在现实生活中,我们永远不会以明文形式存储密码;我们将存储密码的抽象或加密版本。(请参阅本文结尾处的后记)。但是对于此示例,如果我们以明文形式处理密码,则可使设计更易于理解。
<?php // RAY_EE_create.phprequire_once('RAY_EE_config.php');// ACTIVATE THIS TO DROp THE OLD EE_userTable// $mysqli->query("DROP TABLE EE_userTable");$sql= "CREATE TABLE EE_userTable( _key INT NOT NULL AUTO_INCREMENT, uid VARCHAr(16) NOT NULL DEFAULT '?', pwd VARCHAr(16) NOT NULL DEFAULT '?', uuk VARCHAr(32) NOT NULL DEFAULT '?', PRIMARY KEY (_key))";if (!$res = $mysqli->query($sql)) trigger_error( $mysqli->error, E_USER_ERROR );
测试工具此时,最好为我们的新脚本和我们的新数据库表创建测试平台。我们将需要两个脚本进行测试。一个将完全由访问控制。另一个将是一个公共页面,该页面仅测试访问控制并使用响应来确定要创建哪种输出。这两个脚本符合这些要求。
<?php // RAY_EE_controlled.phprequire_once('RAY_EE_config.php');// ACCESS TO THIS PAGE IS CONTROLLED$uid = access_control();echo "<br/>HELLO $uid AND WELCOME TO THE ACCESS ConTROLLED PAGE";
<?php // RAY_EE_public.phprequire_once('RAY_EE_config.php');// ACCESS TO THIS PAGE IS TESTED BUT NOT ConTROLLEDif ($uid = access_control(TRUE)){ echo "<br/>HELLO $uid AND WELCOME TO THE PUBLIC PAGE";}else{ echo "<br/>HELLO STRANGER."; echo "<br/>YOU MIGHT WANT TO <a href="RAY_EE_register.php">REGISTER</a> ON THIS SITE"; echo "<br/>IF YOU ARE ALREADY REGISTERED, YOU CAN <a href="RAY_EE_login.php">LOG IN HERE</a>";}
方便客户使用-注册页面如果我们只有几个用户,则可以使用phpMyAdmin手动注册所有用户,但是如果我们有一个面向公众的网站,则将需要很多工作。相反,我们想要创建一个页面,以便我们的用户可以注册自己。下面的页面将为我们完成注册工作。补充说明:如果您想稍微扩展一下这个概念,此处的EE文章可能对您有所帮助。HTTP://www.experts-exchang e.com/Web_发展吨/ Web_Lang uages-斯坦dards / PHP / A_3939-Reg中istration-和-的电子邮件- /confirm/iati上功能于PHP。html与我们所有的脚本一样,此脚本以PHP命令开始,以第2行的方式加载“ config”脚本require_once('RAY_EE_confi g.php'),我们使用include()函数的require形式,因为脚本无法运行没有我们的配置文件。我们使用include()函数的一次形式,因为配置文件包含函数定义,并且任何重新定义PHP函数的尝试都将导致致命错误。 要开始注册过程,假设没有错误,我们乐观地将$ err变量设置为NULL(第5行)。我们稍后可以检查它是否有任何错误。然后,我们查看$ _POST数组以查看是否拥有处理注册所需的所有信息。如果我们还没有注册信息,该脚本将落到第53行,其中将显示注册表格。如果我们拥有表格中的所有注册信息,我们可以尝试创建客户记录。创建客户记录的第一步是努力清理外部输入。除了这里的基础知识,我们没有做任何其他事情;在现实生活中,可能会有更广泛的过滤和测试。但是对于此示例,我们只是通过转义从表单输入接收到的三个文本字段来保护数据库。 我们的第一个重要测试是查看两个密码字段是否匹配。我们这样做是因为客户端正在键入type =“ password”的表单输入字段,并且浏览器将在输入时掩盖输入内容。为了让我们的客户满意,我们要求她输入两次密码,然后检查是否匹配。如果她以相同的方式两次输入密码,我们可以肯定的是,打字错误不会导致将伪造的密码分配给她的帐户。如果不匹配,我们将错误消息附加到$ err变量。接下来,我们检查数据库以查看是否已使用UID。如果是这种情况,我们将其视为错误,并且将设置错误指示器的方式与设置密码不匹配的错误指示器的方式相同-通过将错误消息添加到$ err变量的末尾。(您可能考虑使用UNIQUE标记MySQL表中的uid列。如果尝试将重复的信息放入UNIQUE列中,MySQL将抛出错误#1062)。编辑完成后(第25行),我们将测试$ err值。由于使用PHP if()语句测试时,NULL字符串将返回FALSE,因此我们可以简单地检查$ err是否为空。我们使用PHP的“ not”表达式,即感叹号。如果我们有错误,则该测试将失败,代码将下降至第48行,在此处显示错误消息并再次出示注册表格。如果没有阻止注册的错误,我们可以在第28行继续进行处理。 我们结合使用用户ID,密码和随机数来创建一个完全唯一且不可预测的值。这是我们可以在cookie中使用的“唯一用户密钥”。为什么不只使用数据库中的auto_increment键?因为cookie中的可预测值会引起黑客入侵。例如,如果黑客看到其cookie值为“ 123”,则可能会尝试将其cookie更改为“ 122”,然后看看会发生什么。当我们使用唯一数据组合的MD5()哈希值时,我们很难猜测被黑客入侵的cookie中可能起作用的值。我们的查询将用户ID,密码和唯一的用户密钥放入表中,并且我们的注册已完成。 我们的客户现在可以登录。但是,为什么要让他们在注册后分别登录?仅需一行代码(第33行),我们便可以在注册时完成其登录。我们一直希望对我们的客户好!接下来,我们将注意力转向客户是否希望我们记住登录状态的问题。如果选中了“ rme”复选框,它将在$ POST数组中设置,我们可以对其进行测试以查看是否需要记住客户端。(与type =“ text”的空输入字段不同,未选中的type =“ checkbox”字段根本不会出现在$ _POST数组中)。因此,如果存在“ rme”,我们将调用Remember_me()函数并将其传递给唯一的用户密钥。如果没有,我们将跳过此步骤,并且不会设置任何cookie。现在我们的注册和登录工作已经完成,我们在第42行欢迎客户并在第44行结束脚本。这是注册脚本:
<?php // RAY_EE_register.phprequire_once('RAY_EE_config.php');// WE ASSUME NO ERRORS OCCURRED$err = NULL;// WAS EVERYTHING WE NEED POSTED TO THIS script?if ( (!empty($_POST["uid"])) && (!empty($_POST["pwd"])) && (!empty($_POST["vwd"])) ){ // YES, WE HAVE THE POSTED DATA. ESCAPE IT FOR USE IN A QUERY $uid = $mysqli->real_escape_string($_POST["uid"]); $pwd = $mysqli->real_escape_string($_POST["pwd"]); $vwd = $mysqli->real_escape_string($_POST["vwd"]); // DO THE PASSWORDS MATCH? if ($pwd != $vwd) $err .= "<br/>FAIL: CHOOSE AND VERIFY PASSWORDS DO NOT MATCH"; // DOES THE UID ALREADY EXIST? $sql = "SELECT uid FROM EE_userTable WHERe uid = '$uid' LIMIT 1"; if (!$res = $mysqli->query($sql)) trigger_error( $mysqli->error, E_USER_ERROR ); $num = $res->num_rows; if ($num) $err .= "<br/>FAIL: UID $uid IS ALREADY TAKEN. CHOOSE ANOTHER"; // IF THERE WERE NO ERRORS THAT PREVENT REGISTRATION if (!$err) { // MAKE THE UNIQUE USER KEY $uuk = md5($uid . $pwd . rand()); $sql = "INSERT INTO EE_userTable (uid, pwd, uuk) VALUES ('$uid', '$pwd', '$uuk')"; if (!$res = $mysqli->query($sql)) trigger_error( $mysqli->error, E_USER_ERROR ); // STORE THE USER-ID IN THE SESSION ARRAY $_SESSION["uid"] = $uid; // IS THE "REMEMBER ME" CHECKBOX SET? if (isset($_POST["rme"])) { remember_me($uuk); } // REGISTRATION AND LOGIN COMPLETE echo "<br/>WELCOME $uid. REGISTRATION COMPLETE. YOU ARE LOGGED IN."; echo "<br/>CLICK <a href="/">HERE</a> TO GO TO THE HOME PAGE"; die(); } // IF THERE WERE ERRORS else { echo $err; echo "<br/>SORRY, REGISTRATION FAILED"; }} // END OF FORM PROCESSING - PUT UP THE FORM?><form method="post">PLEASE REGISTER<br/>CHOOSE USERNAME: <input name="uid" /><br/>CHOOSE PASSWORD: <input name="pwd" type="password" /><br/>VERIFY PASSWORD: <input name="vwd" type="password" /><br/><input type="checkbox" name="rme" />KEEP ME LOGGED IN (DO NOT CHECK THIS ON A PUBLIC COMPUTER)<br/><input type="submit" value="REGISTER" /></form>
客户端身份验证-登录页面现在我们可以注册用户,并且我们拥有测试页面,可让我们查看实际的注册,我们需要创建登录和注销页面。 登录页面使用类似于注册页面的结构。我们需要我们的“配置”页面,然后进行测试以查看是否提供了必要的凭据(第5行)。我们过滤并清理外部输入(第8-9行),然后查询数据库,在UID和PWD字段(第12-20行)中查找完全匹配的一个。如果找不到在UID和PWD上都匹配的行,则第20行的if()语句将失败,脚本将下降至第46行,在此我们可以告诉客户端身份验证失败,并可以显示登录名再次形成。如果确实找到了要查找的一行,则检索该行(第23行)并将UID值复制到会话数组(第26行)中,以显示客户端现在已登录。我们的下一步是查看是否客户选中了“记住我”框。我们测试该复选框(第29行),如果已选中该复选框,我们将调用Remember_me()函数,并传递在注册时创建的唯一用户密钥。Remember_me()函数可在浏览器上设置一个长寿命的cookie。如果没有此cookie,则仅使用会话cookie来记住客户端。当浏览器窗口关闭时,会话cookie也会过期。 登录处理的最后一步是确定客户端下一步要去哪里。我们通过测试会话数组中的“ entry_uri”来做到这一点。如果它是由配置脚本中的access_control()函数设置的,我们可以在header()命令中使用该地址将客户端带回到原始页面。如果未设置该选项(如果客户端直接进入登录页面,则会发生这种情况),我们可以改为重定向到主页。也许应该不用说,但是我还是要说:不要在您的登录脚本中放入access_control()函数,否则您的代码可能会导致服务器循环!这是登录脚本:
<?php // RAY_EE_login.phprequire_once('RAY_EE_config.php');// WAS EVERYTHING WE NEED POSTED TO THIS script?if ( (!empty($_POST["uid"])) && (!empty($_POST["pwd"])) ){ // YES, WE HAVE THE POSTED DATA. ESCAPE IT FOR USE IN A QUERY $uid = $mysqli->real_escape_string($_POST["uid"]); $pwd = $mysqli->real_escape_string($_POST["pwd"]); // ConSTRUCT AND EXECUTE THE QUERY - COUNT THE NUMBER OF ROWS RETURNED $sql = "SELECt uid, uuk FROM EE_userTable WHERe uid = '$uid' AND pwd = '$pwd' LIMIT 1"; $res = $mysqli->query($sql); // IF THE QUERY FAILED, GIVE UP if (!$res) trigger_error( $mysqli->error, E_USER_ERROR ); // THERE SHOULD BE ONE ROW IF THE VALIDATION WAS PROCESSED SUCCESSFULLY $num = $res->num_rows; if ($num) { // RETRIEVE THE ROW FROM THE QUERY RESULTS SET $row = $res->fetch_assoc(); // STORE THE USER-ID IN THE SESSION ARRAY $_SESSION["uid"] = $row["uid"]; // IS THE "REMEMBER ME" CHECKBOX SET? if (isset($_POST["rme"])) { remember_me($row["uuk"]); } // REDIRECT TO THE ENTRY PAGE OR TO THE HOME PAGE if (isset($_SESSION["entry_uri"])) { header("Location: {$_SESSION["entry_uri"]}"); exit; } else { header("Location: /"); exit; } } // END OF SUCCESSFUL VALIDATION else { echo "SORRY, VALIDATION FAILED USING $uid AND $pwd n"; }} // END OF FORM PROCESSING - PUT UP THE LOGIN FORM?><form method="post">PLEASE LOG IN<br/>UID: <input name="uid" /><br/>PWD: <input name="pwd" type="password" /><br/><input type="checkbox" name="rme" />KEEP ME LOGGED IN (DO NOT CHECK THIS ON A PUBLIC COMPUTER)<br/><input type="submit" value="LOGIN" /></form>
客户端取消身份验证-退出页面如果我们的客户端登录,并且未选中“记住我”框,则当浏览器窗口关闭或会话垃圾收集例程检测到长时间不活动时,他将自动注销。 (通常约24分钟)。但是,我们的客户可能希望刻意注销,或者可能是在谨慎的情况下在公共计算机上注销。因此,我们还必须提供一个注销脚本,如下所示。 与往常一样,我们的第一步是加载“ config”脚本。接下来,我们从会话数组中收集UID,或设置一个替代值。我们将在“再见”消息中使用此消息,因此,即使客户端连续两次登录注销脚本,或者试图以某种方式在客户端未登录时设法去到那里,我们都尝试选择一个有意义的数据字符串这是在三元运算符语句(第5行)中完成的。 我们处理“记住我” cookie(如果有的话)(第7-12行)。 下一步是清除会话数组(第15行)。这可能看起来有些束手无策,您可能会考虑完全消除该会话是否有意义。如果即使在客户端注销后,会话中仍有其他信息要保留,则可以仅在第15行取消设置($ _SESSION [“ uid”])并跳过其余代码。但是,您永远不要使用unset($ _ SESSION)清除数组。请参阅此处的注意事项:http : //php.net/manual/en/s ession.exa mples.basi c.php最后,您可以使用我们在第5行创建的数据字符串说“再见”。浏览器输出,而是激活标题(“位置:/” 看一下“出口”;最后一行的声明。尽管这里并不是严格要求的,但是在标题(“ Location”)语句之后使用“ exit”是一种养成的好习惯。为什么?因为您的脚本在发送header()之后将继续正常运行,并且将运行一段不可预知的时间-直到浏览器收到该标头并通过重定向停止脚本。有很多适当的header()语句可以作为完整脚本的一部分内联使用,但是当您使用旨在作为脚本中最后一条语句的语句时,则需要采取额外的步骤来确保它是,实际上是最后一条要执行的语句。这是注销脚本:
<?php // RAY_EE_logout.phprequire_once('RAY_EE_config.php');// GRAB THE UID OR A ConSTANT FOR THE GOODBYE MESSAGE$uid = (isset($_SESSION["uid"])) ? ', ' . $_SESSION["uid"] : ' NOW';// IF THE "REMEMBER ME" cookie IS SET, FORCE IT TO EXPIRE$cookie_expires= time() - date('Z') - REMEMBER;if (isset($_cookie["uuk"])){ setcookie("uuk", '', $cookie_expires, '/');}// CLEAR THE INFORMATION FROM THE $_SESSION ARRAY$_SESSION = array();// IF THE SESSION IS KEPT IN cookie, FORCE SESSION cookie TO EXPIREif (isset($_cookie[session_name()])){ setcookie(session_name(), '', $cookie_expires, '/');}// TELL PHP TO ELIMINATE THE SESSIONsession_destroy();session_write_close();// SAY GOODBYE...echo "YOU ARE LOGGED OUT$uid. GOODBYE.";// OR REMOVE THE GOODBYE MESSAGE AND ACTIVATE THESE LINES TO REDIRECT TO THE HOME PAGE// header("Location: /");// exit;
客户安全-密码页面我们都知道我们应该不时更改密码。为客户提供这种功能非常容易。与注册页面在设计上相似,密码页面采用一对新密码,检查是否匹配并更新数据库以存储新密码。但是有一些重要的区别。每当我们更改数据模型中的某些内容时,我们都需要重新验证客户的密码。为什么?因为我们都是人类,所以我们可能会不小心使计算机保持登录状态。一个偷偷摸摸的人可能会在我们不查看时尝试更改我们的密码(或其他信息)。为了减少这种风险,我们使用与现代ATM控制台所使用的设计模式相似的设计模式。在每次提款之前,您必须重新输入PIN码。这是一个小麻烦,也是一种强大的安全措施。因此,我们将在“更改密码”脚本中执行相同的操作。每当我们更改客户记录中的重要内容(例如电子邮件地址或送货地址)时,我们都将使用此设计模式。我们的第一步是加载“ config”脚本,并调用访问控制功能以检索客户端uid。我们假设没有发生错误(第8行)。我们要求客户端提供三项信息-旧密码和新密码,键入两次以确保正确键入新密码。如果我们拥有所有这三个条件(第11行),则可以开始处理请求。我们清理在查询中使用的值(第14-17行)。接下来,我们测试两个密码字段是否匹配。我们这样做是因为客户端正在键入type =“ password”的表单输入字段,并且浏览器将在输入时掩盖输入内容。为了使我们的客户满意,我们要求她两次输入密码,然后检查是否匹配(第20行)。如果不匹配,我们将错误消息附加到$ err变量。接下来,我们检查数据库,以确保UID记录与旧密码一起存在。用户表中应该只有这样一行。如果不是这种情况,我们将其视为错误,并以与为密码不匹配设置错误指示符相同的方式设置错误指示符(第26行)-通过将错误消息添加到密码末尾$ err变量。编辑完成后(第29行),我们将测试$ err值。如果我们有错误,则该测试将失败,代码将下降至第42行,在此处显示错误消息并再次显示密码表单。如果没有阻止密码更改的错误,我们可以在第32行继续进行处理。 请注意,第32行的UPDATe查询中的WHERe子句–与我们在第23行的SELECT查询中使用的WHERe子句完全相同。我们已经知道只有一行满足该WHERe子句。因此,我们确定将使用新密码更新正确的行。为了表示MySQL的性能,我们告诉数据库限制为一行。这避免了表扫描。更新匹配行后,MySQL将在知道其工作完成后停止运行。密码更改完成,我们向客户发送成功消息(第36行)。如果您的实际实现在用户表中有客户的电子邮件地址(几乎可以肯定),那么您可以扩展此地址以向她发送有关密码更改的电子邮件。良好的安全做法会阻止您发送实际密码。
<?php // RAY_EE_password.phprequire_once('RAY_EE_config.php');// ACCESS TO THIS PAGE IS CONTROLLED$uid = access_control();// WE ASSUME NO ERRORS OCCURRED$err = NULL;// WAS EVERYTHING WE NEED POSTED TO THIS script?if ( (!empty($_POST["old"])) && (!empty($_POST["pwd"])) && (!empty($_POST["vwd"])) ){ // YES, WE HAVE THE NEEDED DATA. ESCAPE IT FOR USE IN A QUERY $uid = $mysqli->real_escape_string($uid); $old = $mysqli->real_escape_string($_POST["old"]); $pwd = $mysqli->real_escape_string($_POST["pwd"]); $vwd = $mysqli->real_escape_string($_POST["vwd"]); // DO THE PASSWORDS MATCH? if ($pwd != $vwd) $err .= "<br/>FAIL: CHOOSE AND VERIFY PASSWORDS DO NOT MATCH"; // DOES THE UID AND OLD PASSWORD COMBINATION EXIST? $sql = "SELECT uid FROM EE_userTable WHERe uid = '$uid' AND pwd = '$old' LIMIT 1"; if (!$res = $mysqli->query($sql)) trigger_error( $mysqli->error, E_USER_ERROR ); $num = $res->num_rows; if ($num != 1) $err .= "<br/>FAIL: $uid DOES NOT HAVE PASSWORD $old"; // IF THERE WERE NO ERRORS TO PREVENT THE PASSWORD CHANGE if (!$err) { // UPDATE THE TABLE TO CHANGE THE PASSWORD $sql = "UPDATE EE_userTable SET pwd = '$pwd' WHERe uid = '$uid' AND pwd = '$old' LIMIT 1"; if (!$res = $mysqli->query($sql)) trigger_error( $mysqli->error, E_USER_ERROR ); // PASSWORD CHANGE IS COMPLETE echo "<br/>THANK YOU, $uid. PASSWORD CHANGE IS COMPLETE."; echo "<br/>CLICK <a href="/">HERE</a> TO GO TO THE HOME PAGE"; die(); } // IF THERE WERE ERRORS else { echo $err; echo "<br/>SORRY, PASSWORD CHANGE FAILED"; }} // END OF FORM PROCESSING - PUT UP THE FORM?><form method="post">CHANGE YOUR PASSWORD<br/>FORMER PASSWORD: <input name="old" type="password" /><br/>CHOOSE PASSWORD: <input name="pwd" type="password" /><br/>VERIFY PASSWORD: <input name="vwd" type="password" /><br/><input type="submit" value="CHANGE" /></form>
简介-付诸实践这就是使用基本的PHP身份验证来密码保护网页的所有步骤。一旦有了这种结构,就可以显示公共注册和登录页面,并通过公共,受保护和部分受保护的网页的组合来构建站点。最重要的是,您可以使用一行PHP代码进行身份验证测试。这些脚本使用PHP会话来标识已登录的客户端。客户可以要求您的网站记住他们的状态,您可以帮他们。他们可以随时注销,也可以在一段时间不活动后自动注销。他们可以随时更改密码来保护自己的帐户信息。此处的注释和代码将在大多数PHP安装中正常运行,但是它们仅作为教学示例,并不打算在生产环境中“按原样”使用-因此,请随意复制和修改它们以适合您的特定需求。后记:了解PHP会话尽管这些脚本以合理的方式使用PHP会话,但我发现很容易过分思考PHP会话的工作方式。它们比您预期的要容易得多!您可能需要阅读这两篇文章,以更好地了解我们的PHP客户端身份验证所依赖的基础技术。 关于PHP会话的文章。有关 HTTP客户端/服务器协议的文章。后记:防止自动注册曾经想知道在线论坛如何获得这么多垃圾的伟哥广告吗?广告由“攻击”机器人脚本放置,这些脚本会找到注册表格并注册一个帐户,然后使用该帐户发布不需要的材料。结合使用两种技术可以减少这种入侵的风险。首先是在注册表上使用CAPTCHA测试。第二个是使用“握手”,这需要一个额外的步骤来通过电子邮件中的说明确认注册。这两种方法都可以单独有效。综合起来,它们甚至更有效。后记:MySQL和MySQL i (2014年春季)本文最初写于几年前,当时PHP支持MySQL数据库扩展。此后发生了变化,如果您使用本文的旧版本作为指导,则可能需要更改脚本。幸运的是,如果选择面向对象的MySQL i,则更改非常容易完成。要了解PHP为什么取消MySQL支持,以及如何使脚本保持运行状态,请参阅这篇文章,该文章教您如何将过程式MySQL代码转换为MySQL i或PDO。后记:PHP session_unregister() (2014年秋季)一些过时的代码集包含使用session_unregister()函数的“注销”示例。PHP在很多年前不推荐使用此功能,而在最近将其删除。不幸的是,PHP代码示例没有到期日期,因此您可能会遇到没有任何警告标签的过时代码。如果您有一个使用session_unregister()的脚本,则应使用相同的函数调用参数将函数名称替换为unset()。今后,请勿使用session_unregister(),session_register()或session_is_registered()。要了解本文的其余部分,您可能想刷新有关PHP会话如何工作的记忆。后记:关于存储密码实际上,您不会以明文形式存储客户端密码。您可以使用PHP md5()函数对密码进行编码。 MD5是“消息摘要”的缩写。请在这里阅读评论。我不同意这样的观念,即如果您了解自己在做什么并且正确使用md5(),那么md5()哈希对于大多数用例来说是不够的。但是,许多专家赞成使用密码哈希。或至少是加密。这是我对md5()的看法。1.假设您愚蠢地以明文形式存储了客户端密码。优点:当客户端忘记密码时,脚本可以发送客户端密码。缺点:如果您的数据库遭到破坏,则将公开所有客户端密码。2.假设您没有存储明文密码,但是却愚蠢地存储了密码的md5()摘要。看起来像这样...5f4dcc3b5aa765d61d8327deb8 82cf99...这是“密码”的md5()。您不能仅通过查看就容易判断出此md5()字符串与“ password”匹配,但是md5()摘要的问题在于它们在编程上是幂等的。无论您对“密码”进行散列多少次,都将始终获得相同的md5()字符串。如果两个人选择相同的密码,则md5()字符串将相同。黑客知道最受欢迎的密码md5()字符串,因此,如果您的数据库遭到破坏,则会暴露许多客户端密码。黑客知道多少个普通密码?您会相信数百万吗?你最好相信它!3.如果让客户选择他们自己的密码,而其中一个选择“密码”,则黑客更有可能破译其他常用密码。与流行密码的md5()字符串匹配的暴力破解方法在计算上是微不足道的。每个字典单词与其md5()摘要匹配最多需要几秒钟。4.为了使密码更难破解,一个子行业涌现出了各种各样的伏都教徒和关于密码学的废话。您可以避免废话,并从OWASP项目中了解更多信息。您可以使用密码哈希来增强密码安全性。5.因为md5()是幂等的,所以安全性要求必须采取某些措施来破坏“密码”和5f4dcc3b5aa765d61d8327deb8 82cf99之间的直接链接。那是在编码过程中添加到密码的“盐”。在密码后面附加一个盐字符串,幂等关系不仅需要密码,还需要盐。因此,如果客户端选择“密码”,则服务器将存储诸如“密码XYZ ”之类的md5字符串,其结果摘要为cceef54fde042f058f57108433 8e2c40。比较这些md5字符串,看看是否可以看到关系。我不能6.您的盐不必很容易猜测。但是必须将其存储在某个位置,以使其在脚本执行期间可以编程方式供PHP使用。您可能要小心保护它。如果盐析字符串和算法遭到破坏,则可能暴露您客户的密码。也许您想将其放入存储在WWW根目录上方的PHP脚本中,并通过include()函数将其引入Web根脚本的范围。7.您可以在密码的两端加盐(加盐和胡椒粉))。您可以使用非常长且任意的字符串来放入盐和胡椒。只要将相同的盐和胡椒粉添加到客户端输入密码框中的任何内容,您的md5()算法就会创建幂等消息摘要。您可以使用简单的SQL查询来匹配密码。8. md5()冲突的可能性(两个不同的输入字符串匹配相同的消息摘要)与您的DNA序列遇到其他人的可能性几乎相同。从理论上讲这是可能的,但不要打赌。9.加盐的密码有多坚固?我将向任何人买啤酒,谁能告诉我创建此md5()摘要的原始输入字符串: e0f1299ed629d3c8826e2dd2be 4780cf为了方便您搜索原始输入字符串,这里是md5()算法说明的链接。http://www.faqs.org/rfcs/r fc1321.htm l并且我已经在服务器上安装了此易于使用的脚本。在此处进行实验:http : //www.iconoun.com/dem o / md5.php10.如果您选择了好的盐和胡椒粉字符串并将其保密,则在大多数情况下,您的md5()摘要将足以保护客户端密码。但是你不能解决愚蠢的问题。如果客户选择“密码”,而您的登录过程仅需要一个电子邮件地址和密码,则没有太多保护该客户免于暴露的机会。内部如何对“密码”进行编码并不重要。知道站点中电子邮件地址的任何人都可以尝试将每个人与“密码”或其他常用密码配对,以查看配对是否有效。毫无疑问,如果您的人口足够多,则您的客户社区中会有人使用“密码”,并且她将成为任何攻击的首批受害者之一。11.由于第10个原因,某些密码选择过程要求您使用字母和数字的组合,使用大写和小写等。我觉得这些令人讨厌,并且倾向于使用多字密码短语的概念。而不是一个密码。我敢肯定会有流行和常用的词组,并且明智的选择是在词组中选择随机单词,并包含一些甚至根本不是单词的内容。高盐短语至少与高盐密码一样好。 12. md5()字符串始终为32个字符的十六进制数字,无论输入字符串包含什么内容。因此,数据库存储必须具有32字节的列宽。ArsTechnica的这篇文章提供了内部人士对黑客可能如何攻击您的编码密码的看法。最终,您的密码就像是防火保险箱的门。根据时间和温度对它们进行评级。坚固的门意味着您有更多的时间来焚烧物品。不幸的是,像ArsTechnica所做的那样,每一项练习都将基本密码和密码短语添加到字典中,并且字典攻击是最快成功的。而您要保护的内容至关重要。如果您有保龄球得分,购买历史,医疗记录,财务细节或核发射代码,则安全措施可能会有所不同。
,