初学者容易将Cookie和Session搞混淆,也有不少人简单地把Cookie和Session理解为一种是客户端存储机制、另一种是服务端存储机制。实际上Cookie和Session不只是这么简单的,这一章就来详细讲解下关于Cookie和Session的内容。
14.1 Cookie详解
14.1.1 Cookie的基本概念和设置
Cookie是一种存储在客户端的数据,能存储Cookie的客户端不只是浏览器,但绝大多数情况下都是由浏览器来实现的。浏览器通过HTTP协议和服务端进行Cookie交互。Cookie是独立于语言而存在的,很多种语言都可以设置和读取Cookie。在实现过程中,编程语言是通过指令通知浏览器,然后是浏览器实现设置Cookie的功能的。读取Cookie则是通过浏览器请求服务端时携带的HTTP头部中的Cookie信息得来的。
PHP中可使用setcookie()来设置cookie,语法如下:
setcookie ( string $name
, string $value
= "" , int $expires
= 0 , string $path
= "" , string $domain
= "" , bool $secure
= false
, bool $httponly
= false
) : bool
setcookie可定义Cookie并将其随HTTP头部一起发送给客户端,在设置Cookie之前不能有任何输出。当Cookie被设置后,可在刷新页面后通过$_COOKIE全局数组获得。
第一个参数name是必选参数,表示Cookie的名称,Cookie的值是通过$_COOKIE[name]获得的。
第二个参数设置Cookie的值,存储在客户端。
第三个参数设置Cookie的有效时间,以秒为单位,如果想要删除一个函数可以将Cookie的有效时间设置为当前时间之前,或者使用unset($_COOKIE[name])来删除某个Cookie。如果不设置这个值,当浏览器关闭时,Cookie会随之失效。
参数path设置Cookie的有效目录,如果设置为“/”就表示在当前目录下均可用,如果设置为“/foo/”就表示只有在目录“/foo/”和其子目录(如“/foo/bar/”)下才可。
参数domain设置Cookie的作用域名,默认在本域名下有效。如果设置该值为“http://www.example.com”则该域名下的所有子域名如i.e.w2.http://www.example.com都可使用该cookie。如果要设置一个域名的所有子域名都可使用,设置其值为example.com即可。
参数secure用来设置是否对Cookie进行加密传输,默认为false。如果设置为true,那么只有在使用https的时候才会设置Cookie。
第七个参数如果为true就表示只能通过HTTP协议才能访问该Cookie,意味着客户端JavaScript不可操作这个Cookie。使用此参数可减少XSS攻击的风险。
下面使用PHP分别设置三个Cookie:
<?php
setcookie('name',"chenxioalong");
setcookie('num','100',time()+100,'/foo/');
setcookie("gender",'male',time()+100,'','php.jingpan.io');
print_r($_COOKIE);
第一个Cookie设置名为name、值为chenxiaolong,其他参数都是默认值,表示在当前目录和域名下都有效,且有效时间持续到浏览器关闭。第二个和第三个Cookie的设置只在特定的目录域名和有效时间内才能看到。注意当第一次在浏览器访问这个脚本文件时并不会有任何输出,因为设置完Cookie后需要刷新页面,这样在下次请求时HTTP头部才会携带上一次设置的Cookie信息,这时才能读取到Cookie。
第一次在浏览器访问该脚本的请求消息头(Request Headers)和响应消息头(Response Headers)分别如下:
Request Headers
GET /php_practice_guide/ch14cookie_session/setcookie1.php HTTP/1.1
Host: php.jingpan.io
Connection: keep-alive
Cache-Control: max-age=0
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.141 Safari/537.36 Edg/87.0.664.75
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6
可见其并没有携带任何Cookie信息,说明浏览器并没有向客户端发送任何Cookie信息,而返回的响应消息头中包含了Cookie信息。
Response Headers
HTTP/1.1 200 OK
Date: Thu, 21 Jan 2021 04:35:51 GMT
Server: Apache
Set-Cookie: name=chenxioalong
Set-Cookie: num=100; expires=Thu, 21-Jan-2021 04:37:31 GMT; Max-Age=100; path=/foo/
Set-Cookie: gender=male; expires=Thu, 21-Jan-2021 04:37:31 GMT; Max-Age=100; domain=php.jingpan.io
Keep-Alive: timeout=5, max=100
Connection: Keep-Alive
Transfer-Encoding: chunked
Content-Type: text/html; charset=UTF-8
返回消息头中包含3个Set-Cookie部分,用于通知浏览器设置对应的Cookie。当我们再次刷新页面的时候,可看到请求消息头中携带了Cookie信息。刷新请求得到的请求消息头如下:
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6
Cache-Control: max-age=0
Connection: keep-alive
Cookie: name=chenxioalong; gender=male
Host: php.jingpan.io
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.141 Safari/537.36 Edg/87.0.664.75
可见其中已经携带了Cookie信息。
我们在前面已经讲过,既然PHP和客户端JavaScript都可以操作Cookie,那么用PHP设置的Cookie也可用JavaScript读取到,用JavaScript设置的Cookie也可由PHP读取到。不同的是,PHP设置的Cookie需要在刷新页面后的下一次请求中才有效,而JavaScript设置的Cookie在本次请求中就有效。
下面用JavaScript代码设置Cookie:
cookie.html
<html>
<head>
<script type="text/javascript">
function setCookie(name,value) {
var Days = 30;
var exp = new Date();
exp.setTime(exp.getTime() + Days*24*60*60*1000);
//alert(exp);
//alert(exp.toGMTString());
document.cookie = name + "="+ escape(value) + ";expires=" + exp.toGMTString();
}
function getCookie(name){
var arr,reg=new RegExp("(^|)"+name+"=([^;]*)(;|$)");
if(arr=document.cookie.match(reg))
return unescape(arr[2]);
else
return null;
}
setCookie('test','testhaha');
//alert(getCookie('test'));
//alert(document.cookie.toString());
</script>
</head>
<body>
</body>
<form enctype="multipart/form-data" action="cookie.php" method="POST">
Send this file:<input name="userfile" type="file"/>
<input type="submit" value="Send file"/>
</form>
</html>
cookie.php
<?php
echo $_COOKIE['test'];
浏览器访问本页用JavaScript设置的Cookie会立即生效。我们再来看访问这个页面的请求消息头和响应消息头:
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6
Cache-Control: max-age=0
Connection: keep-alive
Content-Length: 192
Content-Type: multipart/form-data; boundary=----WebKitFormBoundary1BaxU99HrnAiRADj
Cookie: test=testhaha
Host: php.jingpan.io
Origin: http://php.jingpan.io
Referer: http://php.jingpan.io/php_practice_guide/ch14cookie_session/cookie.html
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.141 Safari/537.36 Edg/87.0.664.75
由于使用的是JavaScript在客户端设置的Cookie,所以在本次向服务端发送HTTP请求时就已经携带了Cookie信息。我们再用PHP代码echo $_COOKIE['test'];来获得由JavaScript设置的Cookie,此时可在页面成功打印出名为test的Cookie值。通过这个例子更清晰地知道,Cookie是编程语言通过一些指令告知浏览器,由浏览器实现的,浏览器和服务端进行通信时,HTTP消息头中携带了Cookie信息。
14.1.2 Cookie的应用和存储机制
Cookie经常用来存储一些不敏感的信息,如用来防止刷票、记录用户名、限制重复提交等。这里以防止用户在一分钟之内多次提交为例,代码如下:
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<script type="text/javascript">
function setCookie(name,value) {
var Days = 30;
var exp = new Date();
exp.setTime(exp.getTime() + 60*1000); //过期时间为1分钟
//alert(exp);
//alert(exp.toGMTString());
document.cookie = name + "="+ escape(value) + ";expires=" + exp.toGMTString();
}
function submit(){
if(getCookie('submit')){
alert('you haved submited befor,please submit after one minute');
}else{
setCookie('submit','yes');
alert('submit sucess.');
}
}
function getCookie(name){
var arr,reg=new RegExp("(^|)"+name+"=([^;]*)(;|$)");
if(arr=document.cookie.match(reg))
return unescape(arr[2]);
else
return null;
}
// setCookie('test','testhaha');
// alert(getCookie('test'));
</script>
</head>
<body>
</body>
<button onclick="submit()">提交</button>
</html>
以上代码实现的是防止用户在一分钟之内多次提交表单,当用户第一次提交表单时,设置Cookie有效期为1分钟,当再次提交时判断Cookie是否过期来限制用户的提交。
前面说,Cookie是存储在客户端的一段数据,但是不同的浏览器存储Cookie的地方不同:一种是将Cookie数据保存在文件中,另一种是保存在浏览器内存中。
在Windows系统上(这里以Windows 7为例)。IE浏览器Cookie数据位于%APPDATA%\Microsoft\Windows\Cookies\ 目录中的xxx.txt文件,里面可能有很多个.txt Cookie文件,如C:\Users\yren9\AppData\Roaming\Microsoft\Windows\Cookies\0WQ6YROK.
在IE浏览器中,IE将各个站点的Cookie分别保存为一个XXX.txt这样的纯文本文件;而Firefox和Chrome是将所有的Cookie都保存在一个文件中,该文件的格式为SQLite数据库格式的文件。Firefox的Cookie数据位于%APPDATA%\Mozilla\Firefox\Profiles\ 目录中的xxx.default目录下,名为Cookies.sqlite的文件中,如C:\Users\jay\AppData\Roaming\Mozilla\Firefox\Profiles\ji4grfex.default\cookies.sqlite。
在Firefox中查看Cookie,可以选择“工具>选项>隐私>显示Cookie”。Chrome的Cookie数据位于%LOCALAPPDATA%\Google\Chrome\User Data\Default\目录中名为Cookies的文件中,如C:\Users\jay\AppData\Local\Google\Chrome\User Data\Default\Cookies。