用户在设计和维护站点的时候,经常需要限制对某些重要文件或信息的访问。通常,我们可以采用内置于WEB服务器的基于HTTP协议的用户身份验证机制。当访问者浏览受保护页面时,客户端浏览器会弹出对话窗口要求用户输入用户名和密码,对用户的身份进行验证,以决定用户是否有权访问页面。
另一方面,在实现用户身份的验证之后,出于各种目的,我们可能还希望了解用户在自己站点上的各种行为,跟踪记录用户访问过的页面和浏览过的内容。虽然,目前各种WEB服务器中都无法提供任何现成的工具帮助用户获取站点访问者的各种统计数据,但是我们还是可以通过有效的设置和使用Cookie实现这一目的。
所谓Cookie就是指那些随同网站的页面内容一起被传送到客户端浏览器,并被保存在客户端的文本短信息。别看Cookie虽小,但是却可以帮助网站设计人员实现包括创建商品购物车,用户社区,个性化站点等在内的众多复杂的应用。
本文将详细介绍如何使用PHP验证用户身份以及如何设置Cookie获取用户信息。最后,我们还将会了解如何使用PHP 4中最受瞩目的新增功能—Session
使用PHP实现简单的用户身份验证
通常,HTTP采用一种挑战/响应模式对试图进入受密码保护区域的用户进行身份验证。具体来说,当用户首次向WEB服务器发出访问受保护区域的请求时,挑战进程被启动,服务器返回特殊的401标头,表明该用户身份未经验证。客户端浏览器在检测到上述响应之后自动弹出对话框,要求用户输入用户名和密码。用户完成输入之后点击确定,其身份识别信息就被传送到服务端进行验证。如果用户输入的用户名和密码有效,WEB服务器将允许用户进入受保护区域,并且在整个访问过程中保持其身份的有效性。相反,若用户输入的用户名称或密码无法通过验证,客户端浏览器会不断弹出输入窗口要求用户再次尝试输入正确的信息。整个过程将一直持续到用户输入正确的信息位置,也可以设定允许用户进行尝试的最大次数,超出时将自动拒绝用户的访问请求。
上述过程虽然烦琐,但是我们可以使用简短的PHP代码,发送HTTP标头,在客户端自动弹出用户名和密码输入窗口,实现整个HTTP的验证过程。PHP中,客户端用户输入的信息传送到服务端之后自动保存在$PHP_AUTH_USER, $PHP_AUTH_PW, 以及 $PHP_AUTH_TYPE这三个全局变量中。利用这些变量,我们就可以根据实现保存在数据文件或数据库中的用户帐号信息验证用户身份。
这里需要提醒使用者注意的一点是,只有在以模块模式安装的PHP中才能使用$PHP_AUTH_USER, $PHP_AUTH_PW, 以及 $PHP_AUTH_TYPE这三个变量。如果用户使用的是CGI模式的PHP则无法实现验证功能。
下面,我们就来详细介绍一下如何使用PHP对用户身份进行验证。
首先,我们可以使用以下代码确定用户是否已经输入了用户名和密码,并显示出用户输入的信息。
if (!isset($PHP_AUTH_USER)) {
header('WWW-Authenticate: Basic realm="My Private Stuff"');
header('HTTP/1.0 401 Unauthorized');
echo 'Authorization Required.';
exit;
}
else {
echo "<P>You have entered this username: $PHP_AUTH_USER
You have entered this password: $PHP_AUTH_PW
The authorization type is: $PHP_AUTH_TYPE</p>";
}
?>
说明:
isset()函数用于确定某个变量是否已被赋值。根据变量值是否存在,返回true或false。
header()函数用于发送特定的HTTP标头。注意,使用header()函数时,一定要在任何产生实际输出的HTML或PHP代码前面调用该函数。
虽然上述代码相当简单,没有根据任何实际值对用户输入的用户名和密码进行有效验证,但是至少我们了解了如何使用PHP在客户端产生输入对话框。
下面,我们就来了解一下如何根据指定的验证信息核实用户身份。代码如下:
<?php
if (!isset($PHP_AUTH_USER)) {
header('WWW-Authenticate: Basic realm="My Private Stuff"');
header('HTTP/1.0 401 Unauthorized');
echo 'Authorization Required.';
exit;
}
else if (isset($PHP_AUTH_USER)) {
if (($PHP_AUTH_USER != "admin") || ($PHP_AUTH_PW != "abc123")) {
header('WWW-Authenticate: Basic realm="My Private Stuff"');
header('HTTP/1.0 401 Unauthorized');
echo 'Authorization Required.';
exit;
} else {
echo "<P>You're authorized!</p>";
}
}
?>
同前面介绍过的一样,我们首先检查用户是否已经输入了用户名称和密码,如果没有则弹出相应对话框要求用户输入身份信息。随后,我们通过判断用户输入的信息是否符合admin/abc123这一指定用户帐号来授予用户访问权限或提示用户再次输入正确的信息。这种方法适用于所有用户都使用同一登录帐号的网站。
到目前为止,我们已经了解了如何使用PHP根据指定帐号核实用户身份。现实的网络中,我们可能会遇到需要根据多个帐号验证用户身份的问题,下面,我们就来具体研究一下如何根据数据文件或数据库信息验证用户身
根据数据文件验证用户身份
这里,我们假设在/path/passwd.txt文件中保存了如下网站用户的帐号信息,其中,每一行分别都是由用户帐号的用户名和密码组成,中间用冒号分开。
Peter:ai890d
John:29hj0jk
Sindy:fsSS92
Jordan:2NNg8ed
Mike:a76zFs
为了能够有效的利用数据文件中的信息,我们需要从数据文件中提取出每个帐号的用户名和密码以便与$PHP_AUTH_USER和$PHP_AUTH_PW变量进行比较。
首先,我们编写如下代码打开数据文件,并将文件内容读取到$contents变量中。 $filename = "/path/passwd.txt";
$fp = fopen($filename, "r");
$contents = fread($fp, filesize($filename));
fclose($fp);
说明: fopen()函数用于打开指定的文件或URL,并返回文件标识。一般使用格式为fopen(文件名, 打开方式)。其中,文件名称可以是以http://或ftp://开头的URL地址。fopen()函数的打开模式主要包括 “r”, “w” 以及 “a”,分别代表打开文件并对文件执行读,写或附加操作。
fread()函数用于读取用fopen()函数打开的文件,并将文件内容以长字符串形式保存。一般使用格式为fread(文件标识,读取长度)。
filesize()函数用于计算指定文件的大小,并返回具体数值。
fclose()函数用于关闭使用fopen()函数打开的文件。
通过上述代码,我们就将数据文件/path/passwd.txt中的用户信息以长字符串的形式保存在了$contents变量中,原数据文件中的不同记录以回车符进行分隔。下面,我们就要从$contents变量中提取出用户帐号的用户名称和密码。
我们可以使用PHP的explode()函数实现上述功能。explode()函数可以根据用户指定的模式对字符串进行分割,并将所有分离出来的子字符串以数组形式保存。举例来说,如果我们使用explode()函数将name1; name1;name3这一字符串按照分号进行分割的话,我们就将会得到一个包含3个元素的数组,元素值分别为name1,name2,和name3。
首先,我们使用explode()函数以回车符为分割标志,将原数据文件中的每一行记录作为一个元素保存到数组中。代码如下:
$line = explode("\r", $contents);
说明: PHP中,我们可以使用"\r"代表回车符。
$line数组中的每一个元素就是一组具体的用户帐号,如Peter:ai890d等。现在,我们需要对结果进行进一步的分离,以获取单独的用户名称和密码。代码如下:
while($i <= sizeof($line)) {
$data_pair = explode(":", $line[$i]);
if (($data_pair[0] = = "$PHP_AUTH_USER") && ($data_pair[1] = = "$PHP_AUTH_PW")) {
$auth = 1;
break;
} else {
$auth = 0;
}
$i++;
}
说明:为了确保explode()函数结果数组中的包含用户名和密码2个元素,我们使用了while循环。 sizeof()函数是PHP的一个数组函数,用于计算数组中的元素个数,并返回结果数值。
上述循环代码中,我们创建了$auth变量,若$auth变量的值为1,则说明用户输入的信息与授权帐号一致;若$auth变量的值为0,则说明用户没有输入正确信息,需要再次重复输入。我们可以使用以下代码,根据$auth变量的值进行选择。
if ($auth == "1") {
echo "You're authorized!";
exit;
} else {
header('WWW-Authenticate: Basic realm="My Private Stuff"');
header('HTTP/1.0 401 Unauthorized');
echo 'Authorization Required.';
exit;
}
根据数据文件验证用户身份的整个过程的代码如下:
<?php
if (!isset($PHP_AUTH_USER)) {
header('WWW-Authenticate: Basic realm="My Private Stuff"');
header('HTTP/1.0 401 Unauthorized');
exit;
} else if (isset($PHP_AUTH_USER)) {
$filename = "/path/passwd.txt";
$fp = fopen($filename, "r");
$contents = fread($fp, filesize($filename));
fclose($fp);
$line = explode("\n", $file_contents);
$i = 0;
while($i <= sizeof($line)) {
$data_pair = explode(":", $line[$i]);
if (($data_pair[0] = = "$PHP_AUTH_USER") && ($data_pair[1] = =
"$PHP_AUTH_PW")) {
$auth = 1;
break;
} else {
$auth = 0;
}
$i++;
}
if ($auth = = "1") {
echo "<P>You're authorized!</p>";
exit;
} else {
header('WWW-Authenticate: Basic realm="My Private Stuff"');
header('HTTP/1.0 401 Unauthorized');
echo 'Authorization Required.';
exit;
}
?>
根据数据库信息验证用户身份
了解了如何根据指定用户帐号和数据文件验证用户身份之后,我们来看一下如何根据保存在数据库中的数据表信息验证用户身份。这里,我们以目前较为普及的MSSQL数据库为例进行说明。因为PHP具有非常强大的数据库支持功能,所以使用其它数据库管理系统的用户也可以参照本文的讲解,使用相应的数据库函数进行测试。
假设我们在名为Authentication的数据库中建立了名为Accounts的如下数据表:
realname
username
Password
Peter Smith
Peter
Ai890d
John Smith
John
29hj0jk
Sindy Smith
Sindy
FsSS92
Jordan Smith
Jordan
2NNg8ed
Mike Smith
Mike
A76zFs
为了能够使用数据库中的信息,我们需要使用一些基本的SQL语句和PHP提供的MSSQL数据库函数。代码如下:
mssql_connect("hostname", "username", "password")
mssql_select_db("Authentication")
$sql = "SELECT * FROM Accounts
WHERE username='$PHP_AUTH_USER' and password='$PHP_AUTH_PW'";
$result = mssql_query($sql);
$num = mssql_num_rows($result);
说明:mssql_connect()函数用于建立与MSSQL服务器的连接。一般使用格式为mssql_connect(服务器名称, 用户名, 密码)。
mssql_select_db()函数用于打开MSSQL服务器中的指定数据库。
mssql_query()函数用于传送用户编写的MSSQL语句,并返回结果标识。
mssql_num_rows()函数用于计算查询返回结果中的记录条数。
上述代码中,我们将查询结果中返回的记录条数保存在$num变量中,可以根据该变量的值判断用户是否为合法用户。若$num变量的值为1,则说明用户输入了有效的身份信息;若$num变量的值为0,则说明用户输入的信息无法被验证。
根据数据库信息验证用户身份的整体代码如下:
<?php
if (!isset($PHP_AUTH_USER)) {
header('WWW-Authenticate: Basic realm="My Private Stuff"');
header('HTTP/1.0 401 Unauthorized');
exit;
} else if (isset($PHP_AUTH_USER)) {
mysql_connect("hostname", "username", "password")
mysql_select_db("Authentication ")
$sql = "SELECT * FROM Accounts
WHERE username='$PHP_AUTH_USER' and password='$PHP_AUTH_PW'";
$result = mysql_query($sql);
$num = mysql_numrows($result);
if ($num != "0") {
echo "<P>You're authorized!</p>";
exit;
} else {
header('WWW-Authenticate: Basic realm="My Private Stuff"');
header('HTTP/1.0 401 Unauthorized');
echo 'Authorization Required.';
exit;
}
}
?>
到此为止,我们已经了解了如何利用PHP根据各种可能的信息源对站点访问用户的身份进行验证,从而保证只有那些被授权的合法用户才能够访问站点的内容。在当今站点的维护和管理过程中,仅仅对网站访问者的身份进行限制,还不能完全达到最终目的,如果要真正掌握和控制用户的各种行为,还需要实现对用户信息的识别和跟踪。下面,我们就来介绍PHP中如何使用cookie识别和追踪用户信息
妙用cookie
从根本上来说,WWW网络是一个无态的环境,WEB服务器无法识别不同的访问者。当某个用户发出页面请求时,WEB服务器只是简单的进行响应,然后就关闭与该用户的连接。因此,WEB服务器无法记录同一用户发出的不同请求信息。虽然,WEB服务器有此先天不足,但是人们开发出cookie这一有效工具可以有效的保存某一个用户的识别信息,从而实现有态连接。除非cookie超过有效期限,否则用户每次访问站点时,WEB服务器都可以获取先前保存该用户cookie中的信息,从而为实现各种复杂的功能和应用提供了有力的支持。
在目前极为时兴的电子商务站点中,cookie的作用更是不可忽视。利用cookie,我们可以创建网上购物系统,允许用户保存所挑选商品的信息,直到最终购买。这里,我们就主要介绍cookie在创建网上购物车中的重要作用。
开始设置cookie之前,我们需要从总体上确定我们准备如何使用cookie,在何处设置cookie,是否需要在每一个页面都设置cookie,以及使用cookie保存何种信息等问题。不管实际选择如何,一定要注意必须在向客户端发送任何内容之前发送cookie,这一点和对header()函数的要求是相同的。
下面,我们就来实际看一下如何具体应用cookie。
首先,我们使用cookie为访问站点的每一为用户设置一个随机生成的用户ID,以方便今后根据ID号识别该用户。代码如下:
<?php
if (!isset($id)) {
srand((double)microtime()*1000000);
$randvalue = rand();
setcookie("id",$randvalue,time()+14400,"/",".domain.name",0);
}
?>
说明:
srand()和rand()函数用于设置随机数字生成器,并产生用户的随机ID号。
setcookie()函数用于设置和发送cookie。一般使用格式为setcookie(名称, 值, 有效期限, 有效路径, 有效域名, 安全传送模式)。setcookie()函数的众多参数中,除名称参数外,其余各项均为可选参数。
这里,我们设置了一个名为id的cookie,并将$randval变量的值赋于该cookie。同时,我们还规定该cookie在4小时之后将被自动清除(现在时间加14400秒),并且在指定域名下的所有页面内有效。这样,当用户再次访问站点页面时,我们就可以直接使用$HTTP_COOKIE_VARS环境数组变量获取先前设定的cookie值,即:
$id = $HTTP_COOKIE_VARS["id"]
随后,我们在名为E-commerce的数据库中建立Items数据表,每当用户把商品放入购物车时,系统自动将该用户的ID号写入数据库:
id
item_id
item_qty
item_color
1404291115
Printer_HP
1
White
随着用户不断挑选新的商品,相应的商品和用户信息就会不断的添加到数据库中。现在,我们可以通过以下代码获取具有特定用户ID号的用户所选购的商品总数:
<?php
$id = $HTTP_COOKIE_VARS["id"]
mssql_connect("hostname", "username", "password")
mssql_select_db("dev_i2ii_com")
$sql = "select sum (item_qty) from Items where id='$id'";
$item_result = mssql_query($sql);
$item_count = mssql_result($item_result,0,"sum(item_qty)");
?>
说明:
mssql_result()函数用于获取数据库查询结果中指定单元格的数据,一般使用格式为mssql_result(结果标识, 行, 列),其中行为整数数值,列为字段偏移或字段名。
这样,我们就把用户所选择的商品总数保存在了$item_count变量中,并可以通过以下代码在站点的每一个页面上显示出来:
echo "购物车中现有: $item_count 件商品。";
上述实例中,我们设定cookie在4小时后被自动清除。除此之外,我们还可以设定当用户退出系统时(例如点击退出按钮)自动清除cookie。要清除某一cookie,只需向客户端发送一个具有相同名称但值为空的cookie即可:
setcookie("id", “” ,time()+14400,"/"," .domain.name ",0);
PHP4 Session功能简介
通过上文对cookie的介绍,大家可能已经体会到保持会话状态的重要性。虽然使用cookie可以保存用户信息,但如果我们希望掌握更为详细的用户数据的话就需要设置多个cookie,对数据库进行多次读写。有没有更加方便而又有效的方式吗?如果大家已经开始使用PHP4的话,相信一定已被其中的session支持功能所深深吸引。
所谓session,从时间角度来说就是指一个用户访问站点的整个持续时间段。在编程环境中,session被作为对象用来保存上述时间段内的各种变量和值。
PHP4中每一个session对象都具有一个标识字串,例如“940f8b05a40d5119c030c9c7745aead9”等。该标识字串可以通过名为PHPSESSID的cookie发送到客户端保存。此外,在服务端还会自动创建以该标识字串命名的临时文件用于保存该session对象中的所有信息。
在使用任何session对象变量之前,一定要首先在该对象中注册所要使用的变量。所有经过注册的变量及变量值都被保存在session的临时文件中。由于session的变量信息并不需要保存在数据库中,所以使用session时不会占用额外的系统资源。
一个典型的session文件的内容可能如下:
count|s:7:"76";
valid|s:7:"yes";
其中count和valid分别是注册变量的名称,而76和yes则是其各自的变量值。使用时可以直接调用变量名。例如,我们可以编写以下代码:
echo "$count";
PHP解释器首先根据客户端的cookie 获取$PHPSESSID变量值,然后搜索以该变量值命名的session临时文件,在文件中找到count变量,并返回变量值。
下面,我们使用session和session变量编写一个简单的访问计数器来进一步的了解session的概念。访问计数器的代码如下:
<?
session_start();
session_register('count');
$count++;
echo "<p>You've been here $count times. Thanks!</p>";
?>
说明:
session_start()函数用于为用户创建新的session对象或继续用户的现有session。
session_register()函数用于在现有session中注册一个或多个变量。整个session过程中,session注册变量的值被一直保存,不会因为用户浏览不同的页面而丢失。
对PHP4的session功能有所了解之后,我们就可以利用session开发出各种个性化的应用。这里,我们将要求用户选择或更改喜爱的页面背景和字体的颜色,然后根据用户的选择进行显示。
首先,我们创建名为session01.php的文件建立session对象并注册$body_color和$text_color两个变量。
<?
session_start();
if (!$PHPSESSID) {
session_register('body_color');
session_register('text_color');
} else if ((!$body_color) || (!$text_color)) {
session_register('body_color');
session_register('text_color');
}
?>
这里,我们使用if/else进行选择判断是为了确保只有在$body_color或$text_color变量不存在的情况下才注册该变量。如果我们只是简单的使用以下方式
<?
session_start();
session_register('body_color');
session_register('text_color');
?>
那么用户每次访问该页面时,原有的$body_color和$text_color变量都将被重新注册,从而无法保留用户以前做出的选择。
下面,我们在该页面中加入可供访问者进行选择的表单。整个页面代码如下:
<?
session_start();
if (!$PHPSESSID) {
session_register('body_color');
session_register('text_color');
} else if ((!$body_color) || (!$text_color)) {
session_register('body_color');
session_register('text_color');
}
?>
<HTML>
<HEAD>
<TITLE>Setting Prefs</TITLE>
</HEAD>
<?
if (!$body_color) {
$body_color = "#FFFFFF";
}
if (!$text_color) {
$text_color = "#000000";
}
?>
<BODY BGCOLOR="<? echo "$body_color"; ?>" TEXT="<? echo "$text_color"; ?>">
<H1>Set Your Color Preferences</h1>
<FORM METHOD="POST" ACTION="session02.php">
<P><strong>Pick a Background Color:</strong>
<input type="radio" name="sel_body_color" value="#FFFFFF">white
<input type="radio" name="sel_body_color" value="#000000">black
<input type="radio" name="sel_body_color" value="#7FFF00">chartreuse
<input type="radio" name="sel_body_color" value="#B0C4DE">light steel blue
<input type="radio" name="sel_body_color" value="#FF6347">tomato
</p>
<P><strong>Pick a Text Color:</strong>
<input type="radio" name="sel_text_color" value="#FFFFFF">white
<input type="radio" name="sel_text_color" value="#000000">black
<input type="radio" name="sel_text_color" value="#F5DEB3">wheat
<input type="radio" name="sel_text_color" value="#BC8F8F">rosy brown
<input type="radio" name="sel_text_color" value="#00FF7F">spring green
</p>
<P><input type="submit" name="submit" value="Set Prefs"></p>
</FORM>
</BODY>
</HTML>
最后,我们编写用于响应用户表单输入的session02.php文件。代码如下:
<?
session_start();
if (!$PHPSESSID) {
session_register('body_color');
session_register('text_color');
} else if ((!$body_color) || (!$text_color)) {
session_register('body_color');
session_register('text_color');
}
$body_color = $sel_body_color;
$text_color = $sel_text_color;
?>
PHP4提供了比cookie更加强大和灵活的session支持功能,极大地方便了WEB应用程序的开发。虽然目前PHP4的测试版本还不可避免的存在某些不足之处,但是相信PHP4正式版的推出,一定会为广大PHP爱好者带来更多的惊喜。