二、ticket生成及检 查
这个子程序实现的功能看起来挺复杂,到现在我都没想到一个合适的词来解释ticket,但是意思倒是很明确,这相当于远端人员的一个用来标识自己身份的入场券,我们不妨仍将其写成ticket。
而这个程序的功能就是一个ticket的生成及检查,而在用户及服务器间传递的就是这个叫做ticket的变量,由于ticket变量具有时效,并且已经过了初步的加密,所以在安全性方面要比直接传递密码要安全得多。至于在编程上倒是没有多少太多的技巧,下面我们仍从编程的角度来看看这段程序。
<?php
class authticket {
var $secret = "setme"; //你需要改变它
var $realm = ""; //身份的领域
var $lifetime = 2 * 60 *60; //时间设置为两小时
var $authenticated = 0; //这个数据当且仅当不等于零时有效
var $identity; //如果解码正确的话,远端的身份
var $issue; //ticket发行时间
var $remote_addr; //用户端的远端地址
var $hash; //hash值
var $autherr; //记录校验失败的原因
// 将ticket数据置零的辅助程序
function zerodata()
{
$this->authenticated = 0;
$this->identity = "";
$this->issue = 0;
$this->remote_addr = "";
$this->hash = "";
$this->autherr = "";
}
//获得身份($identity)、时间($time)以及密钥, 然后产生一个字符串以验证远端
//用户的合法性。这个函数的结果是一个字符串,他可以存在于一个隐藏表甚至是cookie
//中。
//如果$time=0,就取当前时间。
//$identity不能包含“:”,如果有的话,你需要使用一个转义符来代替他。
//在使用的时候要特别小心,我建议只在SSL之后再使用它,除非目前的ticket内容使用了
//比XOR更安全的加密方法。
function makeauth($identity, $time)
{
global $REMOTE_ADDR;
$this->zerodata();
if ($time == 0)
$time = time();
$ticket_items[] = (string)$time;
$ticket_items[] = $this->realm;
$ticket_items[] = $REMOTE_ADDR;
$ticket_items[] = $identity;
$ticket = implode($ticket_items, ":");
$hash = md5($this->secret . $ticket);
$ticket = $hash . ':' . $ticket;
$this->identity = $identity;
$this->issue = $time;
$this->remote_addr = $REMOTE_ADDR;
$this->hash = $hash;
$this->authenticated = 1; /* data is valid */
$this->autherr = "";
return $ticket;
}
//获得通过makeauth()函数产生的($ticket)以及($time),然后效验这个ticket是否有
//效而且未到期。
//如果$time=0,就取当前时间。
//如果检查未通过,这个函数返回一个空串"", $authenticated为零,$autherr返回校
//验失败的原因
//如果检查通过了,ticket中加密的身份被返回,$authenticated非零,$autherr被略过
function checkauth($ticket, $time)
{
global $REMOTE_ADDR;
$this->zerodata();
if ($time == 0)
$time = time();
$ticket_items = explode( ":", $ticket);
if ($ticket_items[3] != $REMOTE_ADDR) {
$this->autherr = "Address mismatch";
return "";
}
if ($this->lifetime != 0)
if ($time > (int)$ticket_items[1] + $this->lifetime) {
$this->autherr = "Ticket expired";
return "";
}
if ($time < (int)$ticket_items[1]) {
$this->autherr = "Ticket used before issued";
return "";
}
if ($this->realm != $ticket_items[2]) {
$this->autherr = "Realm mismatch";
return "";
}
$tmp_items[] = $ticket_items[1];
$tmp_items[] = $ticket_items[2];
$tmp_items[] = $ticket_items[3];
$tmp_items[] = $ticket_items[4];
$tmp_ticket = implode($tmp_items, ":");
$hash = md5($this->secret . $tmp_ticket);
if ($hash != $ticket_items[0]) {
$this->autherr = "Integrity check failed";
return "";
}
$this->hash = $hash;
$this->issue = $ticket_items[1];
$this->remote_addr = $ticket_items[3];
$this->identity = $ticket_items[4];
$this->authenticated = 1;
return $this->identity;
}
};
?>
在程序的一开始,定义了一个类authticket;PHP中的类要比其它的面向对象语言单纯得多,抛开继承(extensions)不谈,在这个程序中我们完全可以把类理解为标识一个对象的全部变量的集合,而相对的function我们也可以把他理解为有关类的一个函数(实际上,这里的function的正规的叫法是叫方法,我们在以后的程序中会看到,这里的function的调用跟其他地方并不完全相同,而且类的变化将返回)。
zerodata提供了一个类的初始化的功能,在这里this表示类本身,也就是说如果调用此方法时是用诸如$tix->zerodata()的形式时,this就是指的tix,this的用法在类的方法中使用非常普遍。 另外,形如$this->authenticated = 0的格式也给我们提供了一个给类中的变量赋值的基本格式,在这里,$this->authenticated确定了类中的一个变量。
makeauth也就是生成ticket了,在这里我们有三个需要注意的地方,一、implode的使用,本函数将数组的内容组合成一个字符串,第一个参数是字之间的分隔符号,第二个参数是数组,与之相对应的函数是explode,本函数将字符串依指定的字符串或字符 separator 切开。将切开后的字符串返回到数组变量中。二、MD5函数的使用,本函数用来计算 MD5 哈稀。是一种加密算法。三、数组的赋值。$ticket_items[] = (string)$time; $ticket_items[] = $this->realm; $ticket_items[] = $REMOTE_ADDR; $ticket_items[] = $identity; 等价于。$ticket_items[0] = (string)$time; $ticket_items[1] = $this->realm; $ticket_items[2] = $REMOTE_ADDR; $ticket_items[3] = $identity;
返加的ticket的内容是md5($this->secret.((string)$time:$this->realm:$REMOTE_ADDR:$identity)): (string)$time:$this->realm:$REMOTE_ADDR:$identity
类的hash值是md5($this->secret.((string)$time:$this->realm:$REMOTE_ADDR:$identity))
checkauth是检查ticket,首先将ticket分为数组,这时:
ticket_items[0]=md5($this->secret.((string)$time:$this->realm:$REMOTE_ADDR:$identity))
ticket_items[1]=(string)$time
ticket_items[2]=$this->realm
ticket_items[3]=$REMOTE_ADDR
ticket_items[4]=$identity
校验不通过的原因分别是:
$ticket_items[3] != $REMOTE_ADDR:地址不对;
$time > (int)$ticket_items[1] + $this->lifetime:过期;
$time < (int)$ticket_items[1]): ticket尚未发行;
$this->realm != $ticket_items[2]) :区域不对;
最后一个错误原因实际上是密钥不对。
在下一个示例程序,我们将看到这个子程序的应用。
php示例程序(三)
三、ticket使用实例
在下面这个程序中,我们将见到我们在第二个示例程序中所讲的ticket的实际应用,通过这个程序,我们将进一步了解ticket。
<HTML>
<HEAD><TITLE>php3 auth library check</TITLE></HEAD>
<?php
include( "authticket.php");
function print_auth($auth, $verf, $tix, $fail)
{
print "<TR>";
if ($tix->authenticated == 0)
if ($fail == "FAIL")
print "<TD>FAIL</TD><TD>FAIL</TD>";
else {
print "<TD><FONT COLOR=/"red/">PASS</FONT></TD>";
print "<TD><FONT COLOR=/"red/">FAIL</FONT></TD>";
}
else
if ($fail == "PASS")
print "<TD>PASS</TD><TD>PASS</TD>";
else {
print "<TD><FONT COLOR=/"red/">FAIL</FONT></TD>";
print "<TD><FONT COLOR=/"red/">PASS</FONT></TD>";
}
print "<TD>$tix->autherr</TD><TD>$verf</TD><TD>$auth</TD></TR>/n";
}
$tix = new authticket;
$tix->secret = "Now is the time for all good men...x";
$tix->realm = "TestRealm";
?>
<BODY>
<table border="1">
<TR><TH COLSPAN="3">Pass/Fail</TH><TH></TH><TH></TH></TR>
<TR><TH>Should</TH><TH>Did</TH><TH>Reason</TH><TH>Identity</TH><TH>Ticket</TH></TR>
<?php
/*
* 下面我们给出对ticket验证的一个最简单的例子
*/
$auth = $tix->makeauth( "explorer", 10);
$verf = $tix->checkauth($auth, 10);
print_auth($auth, $verf, $tix, "PASS");
/*
* 下面我们验证一个过期的ticket
*/
$tix->lifetime = 10; // 有效期10秒
$auth = $tix->makeauth( "explorer", 50); // 时间为50时生成ticket
$verf = $tix->checkauth($auth, 80); // 时间为80时使用ticket
print_auth($auth, $verf, $tix, "FAIL"); // 失败
/*
* 验证一个未发行的ticket
*/
$tix->lifetime = 10; // 有效期10秒
$auth = $tix->makeauth( "explorer", 80); // 时间为80时生成ticket
$verf = $tix->checkauth($auth, 50); // 时间为50时使用ticket
print_auth($auth, $verf, $tix, "FAIL"); // 失败
/*
* 取消有效期检查
*/
$tix->lifetime = 0; // 不设置有效期检查
$auth = $tix->makeauth( "explorer", 50); // 时间为50时生成ticket
$verf = $tix->checkauth($auth, 80); // 时间为90时使用ticket
print_auth($auth, $verf, $tix, "PASS"); // 通过
$tix->lifetime = 10; // 重新设置为效期为10秒
/*
* 下面我们看看如果有人改变了hash会发生什么(在我们的举例中,密钥被改变)
*/
$auth = $tix->makeauth( "explorer", 50);
$tix->secret = $tix->secret . "foobar";
$verf = $tix->checkauth($auth, 55);
print_auth($auth, $verf, $tix, "FAIL"); // 失败
/*
* 改变远端的IP地址
*/
$auth = $tix->makeauth( "explorer", 50);
$REMOTE_ADDR = "512.412.211.212"; // 不合法的IP地址,将无法通过
$verf = $tix->checkauth($auth, 55);
print_auth($auth, $verf, $tix, "FAIL"); // 失败
//
// 不同的身份
//
$auth = $tix->makeauth( "explorer", 0);
$tix->realm = "DifferentRealm";
$verf = $tix->checkauth($auth, 0);
print_auth($auth, $verf, $tix, "FAIL"); // 失败
?>
</table>
</BODY></HTML>
事实上这个程序是我们在这个教程中接触到的第一个真正的动态网页,大家在学习php时,我相信有很多人是向着php的动态特性来,而如何用php来编写一个动态的网页,我们一般的做法是首先将动态网页中的静态内容先分离出来,然后使用我们熟悉的网页制作工具来编写出这个静态的网页,这时候我们的文件的扩展名是.html,现在我们将该文件的扩展名改为.php,这时候,通过服务器的解释,你的这个文件在客户端仍可以正常显示。下面我们可以就开始添加动态的内容了。这时候,我们要用到echo或print,我的习惯是使用echo,在很多场合,echo和print是完全等价的。事实上,你可以把echo和print理解为在php插入html语句的一个关键字,也就是说在<?php ?>括起来的一段php语句中,要插入html语句,必须通过echo和print来实现。
程序执行后,在客户端看到的是这样的HTML源文件:
<HTML>
<HEAD><TITLE>php3 auth library check</TITLE></HEAD>
<BODY>
<table border="1">
<TR><TH COLSPAN="3">Pass/Fail</TH><TH></TH><TH></TH></TR>
<TR><TH>Should</TH><TH>Did</TH><TH>Reason</TH><TH>Identity</TH><TH>Ticket</TH></TR>
<TR><TD>PASS</TD><TD>PASS</TD><TD></TD><TD>explorer</TD><TD>4cd47affcf2605332963b6acc4f8b017:10:TestRealm:61.147.93.2:explorer</TD></TR>
<TR><TD>FAIL</TD><TD>FAIL</TD><TD>Ticket expired</TD><TD></TD><TD>af679e5c5fe2117070b2f186aff33d85:50:TestRealm:61.147.93.2:explorer</TD></TR>
<TR><TD>FAIL</TD><TD>FAIL</TD><TD>Ticket used before issued</TD><TD></TD><TD>84169fe8960e060e3f06fd45462e5389:80:TestRealm:61.147.93.2:explorer</TD></TR>
<TR><TD>PASS</TD><TD>PASS</TD><TD></TD><TD>explorer</TD><TD>af679e5c5fe2117070b2f186aff33d85:50:TestRealm:61.147.93.2:explorer</TD></TR>
<TR><TD>FAIL</TD><TD>FAIL</TD><TD>Integrity check failed</TD><TD></TD><TD>af679e5c5fe2117070b2f186aff33d85:50:TestRealm:61.147.93.2:explorer</TD></TR>
<TR><TD>FAIL</TD><TD>FAIL</TD><TD>Address mismatch</TD><TD></TD><TD>253ccf4eb3fc87ce4336f4088a407b38:50:TestRealm:61.147.93.2:explorer</TD></TR>
<TR><TD>FAIL</TD><TD>FAIL</TD><TD>Realm mismatch</TD><TD></TD><TD>f43eaeec2a52511984438769f65ca3be:979052707:TestRealm:512.412.211.212:explorer</TD></TR>
</table>
</BODY></HTML>