xyhthink

​ Cookie 是服务器保存在浏览器的一小段文本信息,每个 Cookie 的大小一般不能超过4KB。浏览器每次向服务器发出请求,就会自动附上这段信息。

​ Cookie 主要用来分辨两个请求是否来自同一个浏览器,以及用来保存一些状态信息。它的常用来实现免登录功能。

概述

Cookie的常用场合
  1. 对话(session)管理:保存登录、购物车等需要记录的信息。
  2. 个性化:保存用户的偏好,比如网页的字体大小、背景色等等。
  3. 追踪:记录和分析用户行为。
Cookie包含的信息
  1. name (Cookie的名字)
  2. value (Cookie的值,真正的数据保存在这里)
  3. expires (Cookie到期的时间)
  4. domain (域名,默认是当前域名)
  5. path (生效的路径,默认是当前网址)

浏览器可以设置不接受 Cookie,也可以设置不向服务器发送 Cookie。

1
2
// 浏览器是否打开 Cookie 功能
window.navigator.cookieEnabled // true

document.cookie属性返回当前网页的 Cookie。

1
2
// 当前网页的 Cookie
document.cookie

​ 一般来说,单个域名设置的 Cookie 不应超过30个,每个 Cookie 的大小不能超过4KB。超过限制以后,Cookie 将被忽略,不会被设置。

同源限制

​ 浏览器的同源政策规定,两个网址只要域名相同和端口相同,就可以共享 Cookie。不要求协议相同。

Cookie与HTTP协议

​ Cookie 由 HTTP 协议生成,也主要是供 HTTP 协议使用。

Cookie的生成

​ 服务器如果希望在浏览器保存 Cookie,就要在 HTTP 回应的头信息里面,放置一个Set-Cookie字段。

​ HTTP 回应可以包含多个Set-Cookie字段,即在浏览器生成多个 Cookie。

HTTP协议

  1. 渣男协议 => 无状态可靠协议;
  2. HTTP 不知道来的是谁;
  3. HTTP记录来源 => 通过 自报家门 的模式去记录请求的来源。

HTTP 状态解决方案

  1. 会话跟踪技术 ; 给每一个http请求以会话为单位加上一个身份牌。

    身份牌

    听课证的形态 :

    1. 听课证内部存储的信息 : 姓名(自己) , 班级, 学号 。
    • cookie的信息来源来自于多平台 (浏览器端 ,服务端);
    1. 用什么模式存储信息 : 用通用的中国汉字存储信息;
    • cookie字符串 : cookie 会被放置于 http的请求头之中, 编码模式是倾向于URI编码的。
    1. 你的听课证能存下多少信息那 :
    • cookie 有存储数据的最大值 , 4KB , 30条;
    1. 听课证严禁外接 :
    • cookie 有安全性限制 , 可访问可以作用的区间限制, cookie是遵循同源策略的。
    1. 联合办学 :
    • cookie 可以做不同域的数据交互。 domain; => 跨域解决方案。
    1. 时效性 :
    • cookie 有时效性;

Cookie的属性

Expires,Max-Age

Expires属性指定一个具体的到期时间,到了指定时间以后,浏览器就不再保留这个 Cookie。它的值是 UTC 格式,可以使用Date.prototype.toUTCString()进行格式转换。

1
expires = ((d = new Date()).setDate(d.getDate() + options.expires * 864e+5) && d );

​ 如果不设置该属性,或者设为null,Cookie 只在当前会话(session)有效,浏览器窗口一旦关闭,当前 Session 结束,该 Cookie 就会被删除。另外,浏览器根据本地时间,决定 Cookie 是否过期,由于本地时间是不精确的,所以没有办法保证 Cookie 一定会在服务器指定的时间过期。

Max-Age属性指定从现在开始 Cookie 存在的秒数,比如60 * 60 * 24 * 365(即一年)。过了这个时间以后,浏览器就不再保留这个 Cookie。

​ 如果同时指定了ExpiresMax-Age,那么Max-Age的值将优先生效。

Domain, Path

Domain属性指定浏览器发出 HTTP 请求时,哪些域名要附带这个 Cookie。如果没有指定该属性,浏览器会默认将其设为当前域名,这时子域名将不会附带这个 Cookie。

Path属性指定浏览器发出 HTTP 请求时,哪些路径要附带这个 Cookie。只要浏览器发现,Path属性是 HTTP 请求路径的开头一部分,就会在头信息里面带上这个 Cookie。比如,PATH属性是/,那么请求/docs路径也会包含该 Cookie。当然,前提是域名必须一致。

Secure,HttpOnly

Secure属性指定浏览器只有在加密协议 HTTPS 下,才能将这个 Cookie 发送到服务器。另一方面,如果当前协议是 HTTP,浏览器会自动忽略服务器发来的Secure属性。该属性只是一个开关,不需要指定值。如果通信是 HTTPS 协议,该开关自动打开。

HttpOnly属性指定该 Cookie 无法通过 JavaScript 脚本拿到,主要是document.cookie属性、XMLHttpRequest对象和 Request API 都拿不到该属性。这样就防止了该 Cookie 被脚本读到,只有浏览器发出 HTTP 请求时,才会带上该 Cookie。

设置cookie
1
document.cookie = ""key=value;expires=" + 日期对象;";
  • tips: document.cookie 只能设置一条cookie;
区分cookie的不同
  1. cookie 的 name;
  2. cookie 的 path;
删除cookie

把expires过期时间设置成前一天。

Cookie封装

参数列表

1
2
3
( name , value , {expires, path , domain, secure} )
// 当只传入name参数时,执行查找cookie名字代码,并返回;
// 否则设置cookie,并返回新的cookie字符串

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
// cookie查找和添加
cookie( // 参数列表
name , value ,
{
expires,
path ,
domain,
secure
} = { }
){
// 获取cookie的value值;
if( arguments.length === 1){
let [a,res] = [document.cookie.split("; "),""]; // 分割不同部分
res = a.filter( item => item.split("=")[0] === name); // 分割属性属性值,并判断是否为name
return res.length === 0 ? "" : res[0].split("=")[1]; //有则返回
}
var d;
// 设置cookie;
return (document.cookie = [
name + "=" + value,
expires ? ";expires=" + ((d = new Date()).setDate(d.getDate() + expires) && d ): "",
path ? ";path=" + path : "",
domain ? ";domain=" + domain : "",
secure ? ";secure=" + secure : ""
].join(""))
}

// 删除cookie
removeCookie(name , path = ""){
Utils.cookie( name , "" , {
path,
expires : -1
})
}

Cookie免登录

流程思路

  1. 登陆之后 , 后台创建一条cookie , 放在响应头并带到浏览器,浏览器发觉存在cookie那么浏览器会将cookie保存在本地硬盘。

  2. 如果用户访问请求时,先去比对是否存在已经登陆的cookie,如果存在直接登陆。

  3. 浏览器发起一个请求 => 服务器判定是否存在cookie ,存在 cookie 验证cookie

    => 不存在cookie , 走登陆流程,如果成功登陆,创建cookie;

​ 存cookie的加密规范,jwt规范 ; json-web-token;

接口文档

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
- 路径      : http://localhost/gp12coding/0712/login.php
- 请求方式 : POST
- 参数(字段) :
username:"username",
password:"password"
- 返回值 :
{
state : "success|error",
stateCode : 0 ,
[,username : 用户名 [,password : md5加密后的密码]]
}
-stateCode : 0 缺少参数;
1 表示登陆成功;
2 数据库连接失败;
3 用户名不存在;
4 密码错误;

PHP环境搭建

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
header("content-type:text/html;charset=utf8;");
$usr = @$_POST["username"];
$pwd = @$_POST["password"];
$token = @$_COOKIE["TOKEN"]; // 获取指定登录信息TOCKEN

if($tocken && !$pwd && !$usr){ // 如果有tocken并且没有获取用户名密码框内容 ,则返回cookie
die($tocken);
}

if(!$usr || !$pwd ) { // 没有输入用户名密码,则返回
$result = array( "state" => "error" , "stateCode" => 0);
die( json_encode($result) );
}

// 链接数据库
$host = "localhost";
$username = "root";
$password = "root";
$dbname = "gp12";

$conn = mysqli_connect($host,$username,$password,$dbname);

if(!$conn){
// die("数据库连接失败" . mysqli_error());
$result = array( "state" => "error" , "stateCode" => 2 , "errorMsg" => mysqli_error());
die( json_encode($result) );
}
$sql_select = "SELECT username,password FROM gpuserlist WHERE username='$usr'";
// 辨别查询结果之中有多少条数据
$res = mysqli_query($conn,$sql_select);

if(mysqli_num_rows( $res ) === 0 ) {
$result = array( "state" => "error" , "stateCode" => 3 );
die( json_encode($result) );
}else{
while($row = mysqli_fetch_assoc($res)){
if($row["password"] == md5($pwd)){

$result = array( "state" => "success" , "stateCode" => 1 , "username" => $usr , "password" => $row["password"]);
$token = array( "username" => $usr , "password" => $row["password"]);

setcookie("TOKEN",json_encode($tocken),time()+3600 * 24);
die( json_encode($result) );
// 设置一个tocken;

}
}
$result = array( "state" => "error" , "stateCode" => 4 );
die( json_encode($result) );
}

JS代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
// 已将$, ajax, cookie, removeCookie方法封装金Utils里。
let { $, ajax, removeCookie } = Utils;
let [
form,
btn,
username,
password
] = [
$("#form"),
$("#btn"),
$("input[name=username]"),
$("input[name=password]")
]; // 获取参数

//取消默认行为 并添加删除cookie方法
form.addEventListener("submit", evt => {
let e = evt || window.event;
e.preventDefault();
if(e.target.children.length === 1){
removeCookie("TOKEN", "/0712");
location.reload();
}
})

// 登录 查询 验证功能段
btn.addEventListener("click", evt => {
let [usrvalue, pwdvalue] = [username.value, password.value];
// 禁用按钮;
btn.disabled = "disabled";
btn.innerHTML = "loading...";
// 查询数据库;
let url = "http://localhost/0712/login.php";
let data = {
username: usrvalue,
password: pwdvalue
}
ajax(url, {
type: "POST",
data,
dataType: "json",
}).then(res => {
switch (res.stateCode) {
case 0:
alert("登陆参数不全");
break;
case 2:
alert("数据库连接失败");
break;
case 3:
alert("用户名不存在");
break;
case 4:
alert("密码错误");
break;
case 1:
alert("登陆成功");
location.reload();
break;
}
btn.removeAttribute("disabled");
btn.innerHTML = "登陆";
})
})

// 验证程序
function validateTocken() {
// 先发一个请求,验证是否存在tocken ,如果存在就不用登陆了;如果不存在我继续去登陆;
let url = "http://localhost/0712/login.php";
ajax(url).then(res => {
res = JSON.parse(res)
if (res.state) {
return false;
}
form.innerHTML = "用户名为:" + res.username + "<button id='exit'>退出登陆</button>";
})
}

validateTocken(); // 首先执行验证程序。

参考文献

 评论


Power by Yuhangxie , 总访问量为 次 。
载入天数...载入时分秒...
京ICP备19024986号