用户身份验证模块包括4个主要的元素:用户注册、登录和登出、修改密码以及重置密码。我们将按顺序详细讨论每个元素。
27.5.1 注册用户
要注册一个用户,需要通过一个表单获得用户的详细信息,并且将这些信息保存到数据库中。
当用户点击login.php页面上的/"Not a member?/"链接时,就会出现一个由register_form.php产生的注册表单。该脚本如程序清单27-5所示。
程序清单27-5 register_form.php——该表单让用户在PHPbookmark系统中注册
<?php
require_once(/'bookmark_fns.php/');
do_html_header(/'User Registration/');
display_registration_form;
do_html_footer;
?>
可以看到,该页非常简单,只调用了来自output_fns.php的输出库中的函数。该脚本输出如图27-4所示。
图 27-4 注册表单获取了数据库需要的用户详细信息。密码要求输入两次,以防输入错误该页中的灰色表单是由display_registration_form函数输出的,该函数也包含在output_fns.php中。当用户点击/"Register/"按钮时,register_new.php脚本将运行。该脚本如程序清单27-6所示。
程序清单27-6 register_new.php——该脚本验证新用户的数据,并将其存入数据库
<?php
//include function files for this application
require_once(/'bookmark_fns.php/');
//create short variable names
$email=$_POST[/'email/'];
$username=$_POST[/'username/'];
$passwd=$_POST[/'passwd/'];
$passwd2=$_POST[/'passwd2/'];
//start session which may be needed later
//start it now because it must go before headers
session_start;
try{
//check forms filled in
if(!filled_out($_POST)){
throw new Exception(/'You have not filled the form out correctly-
please go back and try again./');
}
//email address not valid
if(!valid_email($email)){
throw new Exception(/'That is not a valid email address.
Please go back and try again./');
}
//passwords not the same
if($passwd!=$passwd2){
throw new Exception(/'The passwords you entered do not match-
please go back and try again./');
}
//check password length is ok
//ok if username truncates,but passwords will get
//munged if they are too long.
if((strlen($passwd)<6)||(strlen($passwd)>16)){
throw new Exception(/'Your password must be between 6 and 16 characters.
Please go back and try again./');
}
//attempt to register
//this function can also throw an exception
register($username,$email,$passwd);
//register session variable
$_SESSION[/'valid_user/']=$username;
//provide link to members page
do_html_header(/'Registration successful/');
echo/'Your registration was successful.Go to the members page to start
setting up your bookmarks!/';
do_html_url(/'member.php/',/'Go to members page/');
//end page
do_html_footer;
}
catch(Exception$e){
do_html_header(/'Problem:/');
echo$e->getMessage;
do_html_footer;
exit;
}
?>
这是该项目中我们看到的第一个比较复杂的脚本。该脚本的起始部分包含了应用程序函数文件并启动了一个会话(用户注册的时候,我们将他的用户名创建为会话变量,正如我们在第23章中“在PHP中使用会话控制”所介绍的那样)。
脚本的主体有一个try语句块,因为需要检查许多条件。如果任何一个条件失败,执行将进入catch语句块,我们将在稍后详细介绍。
接下来验证用户输入的数据。在此过程中,我们要测试许多条件,如下所示。
■检查表单是否完全填写。调用filled_out函数测试,如下所示:
if(!filled_out($_POST))
这个函数是我们自己编写的函数之一。它位于data_valid_fns.php函数库。稍后,我们将详细介绍这个函数。
■检查邮件地址是否有效。测试如下所示:
if(valid_email($email))
这个函数也是我们自己编写的函数之一。它也位于data_valid_fns.php函数库。
■验证用户两次输入的密码是否一致,如下所示:
if($passwd!=$passwd2)
■验证密码长度是否在规定范围之内,如下所示:
if((strlen($passwd)<6)
和
if((strlen($passwd)>16)
在我们的例子中,密码至少为6个字符,这样以防别人容易猜出,同时用户名要少于17个字符,以适合存放到数据库中。请注意,密码的最大长度并不局限于此,因为它是以SHA1哈希值保存的,因此通常是40个字符,而不受密码长度的限制。
本例用到的数据验证函数filled_out和valid_email,分别如程序清单27-7和程序清单27-8所示。
程序清单27-7 data_valid_fns.php文件中的filled_out函数——该函数检查表单是否完全填写
function filled_out($form_vars){
//test that each variable has a value
foreach($form_vars as$key=>$value){
if((!isset($key))||($value==/'/')){
return false;
}
}
return true;
}
程序清单27-8 data_valid_fns.php文件中的valid_email函数——该函数检查邮件地址是否有效
function valid_email($address){
//check an email address is possibly valid
if(ereg(/'^[a-zA-Z0-9_.-][email protected][a-zA-Z0-9-]+.[a-zA-Z0-9-.]+$/',$address)){
return true;
}else{
return false;
}
}
函数filled_out需要传递一个数组变量,通常是$_POST或$_GET变量数组。它检查表单是否完全填写,如果完全填写则返回true,否则返回false。
valid_email函数使用了在第4章“字符串操作与正则表达式”中介绍的正则表达式来验证邮件地址。如果地址是有效的,就返回true,否则返回false。
在验证了输入数据之后,我们就可以尝试注册该用户了。如果回头看看程序清单27-6,会发现我们是按如下方式实现的:
register($username,$email,$passwd);
//register session variable
$_SESSION[/'valid_user/']=$username;
//provide link to members page
do_html_header(/'Registration successful/');
echo/'Your registration was successful.Go to the members page to start
setting up your bookmarks!/';
do_html_url(/'member.php/',/'Go to members page/');
//end page
do_html_footer;
可以看到,我们使用用户输入的用户名、邮件地址和密码作为参数调用了register函数。如果函数执行成功,我们就将用户名注册为会话变量,并为用户提供一个指向成员主页的链接(如果函数执行失败,它将抛出一个可以在catch语句块中捕获的异常)。其输出如图27-5所示。
图 27-5 注册成功,用户可以访问成员页register函数包含在user_auth_fns.php函数库中。函数代码如程序清单27-9所示。
程序清单27-9 user_auth_fns.php文件中的register函数——该函数试图将用户信息提交到数据库
function register($username,$email,$password){
//register new person with db
//return true or error message
//connect to db
$conn=db_connect;
//check if username is unique
$result=$conn->query(/"select*from user where username=/'/".$username./"/'/");
if(!$result){
throw new Exception(/'Could not execute query/');
}
if($result->num_rows>0){
throw new Exception(/'That username is taken-go back and choose another
one./');
}
//if ok,put in db
$result=$conn->query(/"insert into user values
(/'/".$username./"/',sha1(/'/".$password./"/'),/'/".$email./"/')/");
if(!$result){
throw new Exception(/'Could not register you in database-please try again
later./');
}
return true;
}
在这个函数中,也没有特别新的内容,只是将它连接到前面已经建立的数据库中。如果选定的用户名已经存在,或者数据库不能被更新,它将抛出一个异常。否则,它将更新数据库并返回true。
需要注意的是,我们使用了自己编写的函数db_connect来执行数据库连接操作。该函数只提供了一个连接到数据库的地址,而该地址同时还保存着用户名和密码。这样,如果要修改数据库中的密码,只需改变应用程序中的一个文件即可。db_connect函数如程序清单27-10所示。
程序清单27-10 db_fns.php文件中的db_connect函数——该函数连接MySQL数据库
<?php
function db_connect{
$result=new mysqli(/'localhost/',/'bm_user/',/'password/',/'bookmarks/');
if(!$result){
throw new Exception(/'Could not connect to database server/');
}else{
return$result;
}
}
?>
在用户注册之后,可以通过正规的登录或登出页面登录和退出网站。接下来,我们就将实现它。