【PHP基础教程】第五课 Cookie和Session

本课为【PHP基础教程】的最后一课,后面的PHP课程将会逐渐进阶,以达到能够实操的目的。

部分课程将该内容放在后面,但是事实上Cookie和Session在开发网站过程中很常用到。本节课我们将围绕着登录功能对Cookie和Session进行讲解,以增强同学们的理解。

首先我们要了解为什么要使用Cookie和Session——这源于HTTP的特性。HTTP是一种无状态协议,所以需要一些手段来识别用户的身份,常见的方法有HTTP Basic auth(即弹出用户名密码输入框,登路由器的时候大家应该都见过),Bearer auth(通常为API使用,以后会讲),还有就是Cookie和Session了。

Cookie意为“小型文本文件”,所以能储存的容量不大,一般用于存储需要本地暂时或永久保存的信息,例如用户记住密码、Session标识符等等。在我们的例子中,它的作用就是临时储存Session标识符,以及保存用户的登录状态,但是它储存在客户端,容易伪造,相对而言没有那么安全,而且不适用于Cookie功能被关闭的浏览器;Session被称为“会话状态”,它与Cookie的功能类似,但它储存在服务端,无法在客户端进行伪造(注意:这里说的伪造不包括盗取他人的SESSIONID),因此相对安全,且可以不依赖Cookie,也是大多数网站采用的登录状态暂存方法。Session通常在关闭浏览器时失效(这取决于Session的实现方式)。

了解它们以后,就可以开始上手操作了。本节亦没有用到数据库,用户名密码储存在一个关联数组(带键名的数组)中,但实际中是要存在数据库中,学了数据库后可以替换为数据库。

我们先做一个首页,这个首页的作用主要是显示是否登录,以及登录后的用户。新建一个 index.php

<?php session_start(); ?>
<!DOCTYPE html>
<html>
<body>

<p>
<?php if($_SESSION['username']): ?>
<form action="login.php" method="post">
	Welcome! <?php echo $_SESSION['username']; ?>.
	<input type="submit" name="logout" value="登出">
</form>
<?php else: ?>
未登录,<a href="login.php">去登录</a>
<?php endif; ?>
</p>

</body>
</html>

本页面还用到了模板语言的写法,相对整洁。

首先是session_start(),这个函数的作用就是初始化Session,就是处理SESSIONID的,没有ID的客户端分配一个,有的就直接读取之前储存的Session。在php.ini可以设置默认开启session_start,但是保险起见还是在程序中调用这个函数。

然后就是判断Session中有没有username,这里使用超全局变量$_SESSION来获取Session数组。

未登录时的效果:

已登录的效果:

可以从代码中看见我们将登录页面和登录逻辑放到了 login.php 中,因此我们再新建一个 login.php 

<?php
session_start();
if ($_POST['login']) {
	$users = ['admin'=>'123456','user'=>'hello','alice'=>'awsl'];
	if ($_POST['password']==$users[$_POST['username']]) {
		$_SESSION['username']=$_POST['username'];
		die('登录成功,等待3秒或点击<a href="index.php">这里</a>返回首页<meta http-equiv="refresh" content="3;url=index.php">');
	} else {
		echo '登录失败,请检查用户名和密码是否正确';
	}
}
if ($_POST['logout']) {
	unset($_SESSION['username']);
	die('登出成功,等待3秒或点击<a href="index.php">这里</a>返回首页<meta http-equiv="refresh" content="3;url=index.php">');
}
?>

<!DOCTYPE html>
<html>
<body>

<form method="post">
用户名:<br>
<input type="text" name="username">
<br>
密码:<br>
<input type="password" name="password">
<br><br>
<input type="submit" name="login" value="登录">
</form>

</body>
</html>

非常简洁的一个界面:

我们定义键值对数组来储存用户名和密码,亦通过读取数组内容判断用户名密码是否正确。登录成功,将用户名储存到$_SESSION中,跳转到 index.php ,此时 index.php 能够正常读取刚刚储存的用户。

同时,login.php 还承担了登出的功能,登出就是把 $_SESSION['username'] 销毁或者置空。

这个例子很简单,代码都是现成可用的,大家可以自己创建文件体验一下。

Session是用上了,那Cookie呢?别急,大多数网站的登录界面不是还有个“记住我”的选项吗,那个使用的就是Cookie。因为我们现在还没有数据库,所以我们可以暂时以用户名为媒介,在 index.php 就自动登录。

整体的改动不大,但是我们先了解一下Cookie如何生成和使用。

PHP中,使用 setcookie 函数来生成Cookie。函数定义如下:

setcookie ( string $name [, string $value = "" [, int $expire = 0 [, string $path = "" [, string $domain = "" [, bool $secure = false [, bool $httponly = false ]]]]]] ) : bool

一般而言,我们传入3个参数来实现自动登录。

$name就是Cookie键名,$value就是Cookie的值,$expire就是过期时间。注意,这里的过期时间指的是时间戳,而不是过多少秒到期,因此我们采用 time()+second 来表示过期时间;如果设为0,则表示浏览器关闭时失效。

对于我们的例子而言,生成Cookie的语句为:

setcookie('username',$_POST['username'],time()+7*24*3600);

表示将用户名储存在名为username的Cookie中,到期时间是7天后。

我们给表单添加一个“记住我”的checkbox:

<label><input type="checkbox" name="remember">记住我</label>

注意,checkbox组件在不勾选时,不会出现在表单序列中,因此我们常用 isset($_POST['remember']) 判断checkbox是否勾选。默认的值是on。

在给Session赋值的语句后,添加如下代码:

if (isset($_POST['remember'])) {
    setcookie('username',$_POST['username'],time()+7*24*3600);
}

这就是登录时“记住我”的操作。

光有这点还不够,我们还要在 index.php 中添加判断Cookie是否存在的代码:

if (isset($_COOKIE['username'])) {
    $_SESSION['username']=$_COOKIE['username'];
}

这段代码跟在 session_start(); 后即可。可以看到,我们使用$_COOKIE来读取Cookie,这一点与Session是一样的;但是Session可以通过$_SESSION来储存,Cookie只能通过setcookie来储存。

最后一步,在登出时清除Cookie:

setcookie('username', '', time() - 3600);

方法就是将Cookie的过期时间设为系统时间之前,自然而然它就是无效的了。这段代码添加到 unset($_SESSION['username']); 之后。

此时,我们的登录系统就拥有了自动登录的功能。当关闭浏览器时,我们的登录状态没有失效,再次打开浏览器,我们仍然是已登录状态,只有在7天过期后或手动登出时,我们的登录状态才会改变。

完整的代码如下:

index.php:

<?php
session_start();
if (isset($_COOKIE['username'])) {
    $_SESSION['username']=$_COOKIE['username'];
}
?>
<!DOCTYPE html>
<html>
<body>

<p>
<?php if($_SESSION['username']): ?>
<form action="login.php" method="post">
	Welcome! <?php echo $_SESSION['username']; ?>.
	<input type="submit" name="logout" value="登出">
</form>
<?php else: ?>
未登录,<a href="login.php">去登录</a>
<?php endif; ?>
</p>

</body>
</html>

login.php:

<?php
session_start();
if ($_POST['login']) {
	$users = ['admin'=>'123456','user'=>'hello','alice'=>'awsl'];
	if ($_POST['password']==$users[$_POST['username']]) {
		$_SESSION['username']=$_POST['username'];
		if (isset($_POST['remember'])) {
			setcookie('username',$_POST['username'],time()+7*24*3600);
		}
		die('登录成功,等待3秒或点击<a href="index.php">这里</a>返回首页<meta http-equiv="refresh" content="3;url=index.php">');
	} else {
		echo '登录失败,请检查用户名和密码是否正确';
	}
}
if ($_POST['logout']) {
	unset($_SESSION['username']);
	setcookie('username', '', time() - 3600);
	die('登出成功,等待3秒或点击<a href="index.php">这里</a>返回首页<meta http-equiv="refresh" content="3;url=index.php">');
}
?>

<!DOCTYPE html>
<html>
<body>

<form method="post">
用户名:<br>
<input type="text" name="username">
<br>
密码:<br>
<input type="password" name="password">
<br>
<label><input type="checkbox" name="remember">记住我</label>
<br><br>
<input type="submit" name="login" value="登录">
</form>

</body>
</html>

这就是一个简易的登录系统,很好的解释了Session和Cookie的主要用途。Cookie在早期的购物网站还承担购物车一类的强大功能,由于JS的发展,现在都使用 localstorage 来代替。总而言之,它们基本上是网站中不可或缺的模块,十分重要,希望大家能掌握它们的用法。

本例子存在许多漏洞,不宜作为真正的登录模块使用,仅用于教学用途。


PHP基础教程就此结束,在下一阶段的课程中,我们将会以数据库操作为主要内容,进行进一步的学习。我们选用的数据库为MySQL,其他关系型数据库同理,只是部分SQL语法不一样。

推荐预习内容:

SQL 教程 on w3school

PHP PDO

因PHP7废除MySQL库,且推荐使用PDO库,因此我们不再学习MySQL库的使用,而使用PDO库。好处是PDO库可以用于多种数据库的操作,以后的开发中只要使用PDO,就可以轻松切换使用不同的数据库。

发表评论