cookie和会话.docx
《cookie和会话.docx》由会员分享,可在线阅读,更多相关《cookie和会话.docx(51页珍藏版)》请在冰豆网上搜索。
cookie和会话
HTTP是一种无状态技术,这意味着每个单独的HTML页面都是一个无关的实体。
当人们穿过站点时,HTTP没有用于跟踪用户或保持变量的方法。
尽管浏览器会跟踪你访问过的页面,但是服务器不会记录谁看到过什么内容。
如果服务器不能够跟踪用户,就不可能有购物车或自定义个性化Web站点的存在。
使用服务器端技术(如PHP),可以克服Web的无状态性。
可用于此目的的两种最佳的PHP工具是cookie和会话。
你可能已经知道,cookie在用户的Web浏览器中存储数据。
在用户访问产生cookie的站点上的页面时,服务器可以从该cookie读取数据。
会话把数据存储在服务器本身上面。
会话一般比cookie更安全,并且可以存储多得多的信息。
这两种技术都很容易与PHP一起使用,并且值得了解。
在本章中,你将看到cookie和会话的使用。
演示该信息的示例将是一个登录系统,它基于现有的users数据库。
11.1 建立登录页面
登录过程涉及几个组成部分:
l 用于提交登录信息的表单;
l 确认必要信息已提交的验证例程;
l 比较提交的信息与存储的信息的数据库查询;
l
用于存储反映成功登录的数据的cookie或会话。
后续页面然后将包含一些检查,用于确认用户已登录(用于限制对该页面的访问)。
当然,还有一个注销过程,它涉及清除表示登录状态的cookie或会话数据。
首先,让我们获取其中一些常见的元素并把它们存放在单独的文件中。
然后,需要这种功能的页面可以包含必要的文件。
像这样分解逻辑将使得接下来的一些脚本更容易阅读和编写,并有助于减少它们之间的冗余性。
我设计了两个可包含的文件。
第一个文件将包含登录页面的大部分内容,包括:
标题、错误报告、表单和脚注(参见图11-1)。
建立登录页面
(1)在文本编辑器或IDE中创建一个新的PHP页面(参见脚本11-1)。
脚本11-1 login_page.inc.php脚本创建完整的登录页面(包括表单)并报告错误。
需要显示登录页面的其他页面将包含它
1
php#Script11.1-login_page.inc.php
2
3//Thispageprintsanyerrorsassociatedwithloggingin
4//anditcreatestheentireloginpage,includingtheform.
5
6//Includetheheader:
7$page_title='Login';
8include('includes/header.html');
9
10 //Printanyerrormessages,iftheyexist:
11 if(!
empty($errors)){
12 echo'
Error!
13 Thefollowingerror(s)occurred:
';
14 foreach($errorsas$msg){
15 echo"-$msg
\n";
16 }
17 echo'
Pleasetryagain.
';
18 }
19
20 //Displaytheform:
21 ?
>
22
Login
23
24
EmailAddress:
25
Password:
26
27
28
29
30
php//Includethefooter:
31 include('includes/footer.html');
32 ?
>
(2)包括标题。
$page_title='Login';
include('includes/header.html');
本章将利用最初在第3章创建然后在第8章修改过的相同模板系统。
(3)如果有任何出错消息,就打印它们。
if(!
empty($errors)){
echo'
Error!
Thefollowingerror(s)occurred:
';
foreach($errorsas$msg){
echo"-$msg
\n";
}
echo'
Pleasetryagain.
';
}
这段代码也是在第8章中开发的。
如果有任何错误存在(在$errors数组变量中),就会把它们打印成一份无序列表(参见图11-2)。
图11-2 带有错误报告的登录表单和页面
(4)显示表单。
?
>
Login
EmailAddress:
Password:
HTML表单只需要两个文本输入框:
一个用于电子邮件地址,另一个用于密码。
输入框的名称与sitename数据库(登录系统就基于该数据库)的users表中的那些列名称匹配。
为了更容易创建HTML表单,首先关闭PHP部分。
该表单不是黏性表单,但是可以轻松地添加代码来实现这一点(但是仅仅对于电子邮件地址可以如此,密码则不能是黏性的)。
(5)完成页面。
php
include('includes/footer.html');
?
>
(6)将文件另存为login_page.inc.php,将其存放在Web目录中(该目录与第8章中的文件header.html、footer.html和style.css都在includes文件夹中)。
该页面将使用.inc.php扩展名,指示它是一个可包含的文件并且它包含PHP代码。
ü提示
l 这个脚本包含includes目录内的标题和脚注文件似乎不合乎逻辑,因为该脚本也在相同的目录内。
这段代码会工作,因为该脚本将被主目录内的页面包含;因此包含引用是关于父目录的,而不是关于这个目录的。
11.2 创建登录函数
除了存储在login_page.inc.php中的登录页面外,还有一些功能在本章中的多个脚本中也很常见。
在下一个脚本中(登录/注销系统中的其他页面也会包含该脚本),将定义两个函数。
许多页面最终将把用户从一个页面重定向到另一个页面。
例如,在成功登录后,将把用户带到loggedin.php。
如果用户访问loggedin.php,但他们没有登录,就应该把他们带到index.php。
重定向使用第10章中介绍的header()函数。
用于重定向的语法如下:
header('Location:
由于这个函数将把浏览器发送到page.php,应该在这之后立即使用exit()终止当前脚本:
header('Location:
exit();
如果不这样做,当前脚本将会继续运行(只是不在Web浏览器中运行而已)。
header()调用中的位置值应该是绝对URL(
本章中的多个脚本将使用的另外一些代码用于验证登录表单。
这是一个包含三个步骤的过程:
(1)确认提供了电子邮件地址;
(2)确认提供了密码;
(3)确认提供的电子邮件地址和密码与数据库中存储的电子邮件地址和密码匹配(在注册过程中)。
因此,下一个脚本将定义两个不同的函数。
在接下来的步骤中将解释每个函数是如何工作的。
创建登录函数
(1)在文本编辑器或IDE中创建一个新的PHP文档(参见脚本11-2)。
脚本11-2 login_functions.inc.php脚本中定义了两个函数,将被登录/注销过程中的不同脚本使用
1
php#Script11.2-login_functions.inc.php
2
3//Thispagedefinestwofunctionsusedbythelogin/logoutprocess.
4
5/*ThisfunctiondeterminesandreturnsanabsoluteURL.
6*Ittakesoneargument:
thepagethatconcludestheURL.
7*Theargumentdefaultstoindex.php.
8*/
9functionabsolute_url($page='index.php'){
10
11 //StartdefiningtheURL...
12 //URLishttp:
//plusthehostnameplusthecurrentdirectory:
13 $url='http:
//'.$_SERVER['HTTP_HOST'].dirname($_SERVER['PHP_SELF']);
14
15 //Removeanytrailingslashes:
16 $url=rtrim($url,'/\\');
17
18 //Addthepage:
19 $url.='/'.$page;
20
21 //ReturntheURL:
22 return$url;
23
24 }//Endofabsolute_url()function.
25
26
27 /*Thisfunctionvalidatestheformdata(theemailaddressandpassword).
28 *Ifbotharepresent,thedatabaseisqueried.
29 *Thefunctionrequiresadatabaseconnection.
30 *Thefunctionreturnsanarrayofinformation,including:
31 *-aTRUE/FALSEvariableindicatingsuccess
32 *-anarrayofeithererrorsorthedatabaseresult
33 */
34 functioncheck_login($dbc,$email='',$pass=''){
35
36 $errors=array();//Initializeerrorarray.
37
38 //Validatetheemailaddress:
39 if(empty($email)){
40 $errors[]='Youforgottoenteryouremailaddress.';
41 }else{
42 $e=mysqli_real_escape_string($dbc,trim($email));
43 }
44
45 //Validatethepassword:
46 if(empty($pass)){
47 $errors[]='Youforgottoenteryourpassword.';
48 }else{
49 $p=mysqli_real_escape_string($dbc,trim($pass));
50 }
51
52 if(empty($errors)){//Ifeverything'sOK.
53
54 //Retrievetheuser_idandfirst_nameforthatemail/passwordcombination:
55 $q="SELECTuser_id,first_nameFROMusersWHEREemail='$e'ANDpass=SHA1('$p')";
56 $r=@mysqli_query($dbc,$q);//Runthequery.
57
58 //Checktheresult:
59 if(mysqli_num_rows($r)==1){
60
61 //Fetchtherecord:
62 $row=mysqli_fetch_array($r,MYSQLI_ASSOC);
63
64 //Returntrueandtherecord:
65 returnarray(true,$row);
66
67 }else{//Notamatch!
68 $errors[]='Theemailaddressandpasswordentereddonotmatchthoseonfile.';
69 }
70
71 }//Endofempty($errors)IF.
72
73 //Returnfalseandtheerrors:
74 returnarray(false,$errors);
75
76 }//Endofcheck_login()function.
77
78 ?
>
因为这个文件将由其他文件包含,所以它不必包含任何HTML。
(2)开始定义新函数。
functionabsolute_url($page='index.php'){
absolute_url()函数将返回一个绝对URL,它对于运行这些脚本的站点是正确的。
动态执行该操作的好处是(与仅仅硬编码
该函数带有一个可选的参数:
最终的目标页面名称。
默认值是index.php。
(3)开始定义URL。
$url='http:
//'.$_SERVER['HTTP_HOST'].dirname($_SERVER['PHP_SELF']);
首先,把http:
//以及主机名称(它可以是localhost或)赋予$url。
这里使用dirname()函数添加了当前目录的名称,以免在子文件夹内发生重定向。
$_SERVER['PHP_SELF']引用当前脚本(它将是调用这个函数的脚本),包括目录名称。
整个值可能是/somedir/page.php。
dirname()函数将只从那个值中返回目录部分(即/somedir/)。
(4)从URL中删除任何结尾斜杠。
$url=rtrim($url,'/\\');
由于子文件夹可能添加额外的斜杠(/)或反斜杠(\,对于Windows),所以函数需要删除它们。
为了执行该任务,可使用rtrim()函数。
默认情况下,这个函数会删除字符串右边的空格。
如果提供了要删除的字符列表作为第二个参数,就会代之以删除这些字符。
这里要删除的字符应该是/或\。
但是,由于PHP中的反斜杠是转义符,需要使用\\来指示单个反斜杠。
因此,简而言之,如果$url以这些字符中的任何一个结尾,rtrim()函数将删除它们。
(5)添加特定的页面到URL中并完成函数。
$url.='/'.$page;
return$url;
}//Endofabsolute_url()function.
最后,将特定的页面名称追加到$url中。
在它前面放置一个斜杠,因为任何尾随的斜杠都会在第(4)步中被删除,并且不能把page.php作为URL,然后返回该URL。
这些看起来似乎都相当复杂,但它可以非常有效地确保重定向正确工作,而不管在什么服务器上、来自于什么目录,或者脚本是否在运行(只要重定向发生在那个目录内即可)。
(6)开始创建新函数。
functioncheck_login($dbc,$email='',$pass=''){
这个函数将验证登录信息。
它带有三个参数:
数据库连接(它是必需的)、电子邮件地址(它是可选的)和密码(它也是可选的)。
尽管这个函数可以直接访问$_POST['email']和$_POST['pass'],但是更好的做法是给函数传递这些值,从而使得函数更独立。
(7)验证电子邮件地址和密码。
$errors=array();
if(empty($email)){
$errors[]='Youforgottoenteryouremailaddress.';
}else{
$e=mysqli_real_escape_string($dbc,trim($email));
}
if(empty($pass)){
$errors[]='Youforgottoenteryourpassword.';
}else{
$p=mysqli_real_escape_string($dbc,trim($pass));
}
这个验证例程类似于注册页面中使用的那个验证例程。
如果发生任何问题,都会把它们添加到$errors数组中,最终将把它们用在登录页面上(参见图11-2)。
(8)如果没有发生错误,就运行数据库查询。
if(empty($errors)){
$q="SELECTuser_id,first_nameFROMusersWHEREemail='$e'ANDpass=SHA1('$p')";
$r=@mysqli_query($dbc,$q);
这个查询从数据库中选择user_id和first_name值,其中提交的电子邮件地址(来自于表单)匹配存储的电子邮件地址,并且提交的密码的SHA1()版本匹配存储的密码(参见图11-3)。
图11-3 如果用户提交正确的电子邮件地址/密码组合,登录查询就会得到这个结果
(9)检查查询的结果。
if(mysqli_num_rows($r)==1){
$row=mysqli_fetch_array($r,MYSQLI_ASSOC);
returnarray(true,$row);
}else{
$errors[]='Theemailaddressandpasswordentereddonotmatchthoseonfile.';
}
如果查询返回一行,那么登录信息是正确的。
然后将把结果获取进$row中。
成功登录中的最后一步是把两种信息返回到请求的脚本中:
值true和从MySQL获取的数据,其中前者指示登录成功。
使用array()函数,可以返回布尔值和$row数组。
如果查询没有返回一行,就会把一条出错消息添加到数组中。
它最终将显示在登录页面上(参见图11-4)。
图11-4 如果用户输入了电子邮件地址和密码,但是它们与数据库中存储的值不匹配,就会得到这个结果
(10)完成开始于第(8)步中的条件语句,并完成函数。
}//Endofempty($errors)IF.
returnarray(false,$errors);
}//Endofcheck_login()function.
该函数的最后一步是返回值false指示登录失败,以及返回$errors数组,它存储失败的原因。
可以把这个return语句放在这里(函数的末尾,而不是条件语句内)因为如果登录失败,函数将只会执行到这里。
如果登录成功,第(9)步中的return行将阻止函数继续执行(一旦函数执行return语句,它就会停止执行)。
(11)完成页面。
?
>
(12)将文件另存为login_functions.inc.php,并将其存放在Web目录中(该目录与header.html、footer.html和style.css这几个文件都在includes文件夹中)。
该页面也会使用.inc.php扩展名,指示它是一个可包含的文件并且它包含PHP代码。
ü提示
l 本章中的脚本不包含任何调试代码(如MySQL错误或查询)。
如果你有关于这些脚本的任何问题,可以应用第7章中概括的调试技术。
l 可以在header()调用中把name=value对添加到URL中,以把值传递到目标页面:
$url.='?
name='.urlencode(value);
11.3 使用cookie
cookie是服务器在用户的机器上存储信息的一种方式。
利用这种方式,站点可以在访问期间记住或跟踪用户。
可以把cookie看作像名字标签一样,你告诉服务器你的名字,它就会给你戴上一个标签。
这样,它就可以回过头来查阅名字标签,从而知道你是谁。
有些人对cookie心存疑虑,因为他们认为cookie允许服务器知道了他们太多的事情。
不过,cookie只能用于存储提供给服务器的信息,因此,与大多数其他在线技术相比,cookie的安全性并不差。
不幸的是