Cookie学习——从入门到放弃

796 阅读5分钟

写在前面

最近在开发过程中,遇到一些关于cookie的坑,趁此机会对cookie进行梳理一遍,加强记忆,有错误之处,还希望各位大佬予以纠正。

cookie是什么

无论是在面试过程中,还是在开发过程中,我们总是会被问到:cookie是什么?

我的理解是:

  1. cookie是一种传递数据的技术,通过cookie可以将一些数据从前端传递到后端,后端API接口可以通过req.cookies 属性来获取。
  2. cookie是浏览器管理状态的文件,我们可以通过JS代码读写该文件中的内容,达到管理状态的目的。

知道了cookie是什么,那么为什么要使用cookie呢?这是因为http是一个不保存状态的协议,即服务器在处理请求的时候,并不清楚这些请求是否来自同一个浏览器,相关的逻辑不好处理。

于是开发人员就想着:如果我在给服务器发送请求的时候,能传递一些表示状态的值,就可以让服务器知道这些请求来自一个浏览器,那么问题不就解决了吗?于是便产生了cookie这一技术。

cookie的属性

cookie的常见属性有以下五种,这里对其做简单介绍

  1. name: cookie的名字,一个域名下的cookie名字不能重复,否则会被覆盖。

  2. value: cookie的值

  3. domain: cookie绑定的域名,如果没有设置,就会自动绑定到执行语句所在的域名。值得注意的是:,统一个域名下的二级域名也是不可以交换使用cookie的,比如,你设置www.baidu.com和image.baidu.com,依旧是不能公用的。

  4. path: cookie所采的目录,默认值是/(即根目录)。cookie目录有管理cookie访问权限的作用,比如:将cookie1的path设置为/root/,将cookie2的path设置为/root/book。

    那么/root/下的所有页面都有权限访问cookie1,/root/keys/、/root/phones/下的页面没有权限访问cookie2,只有/root/book/下的页面才有权限访问cookie2。

  5. Expires: cookie的过期时间,后文会进行详细介绍。

  6. Security: Cookie的Secure属性,意味着保持Cookie通信只限于加密传输,指示浏览器仅仅在通过安全/加密连接才能使用该Cookie。如果一个Web服务器从一个非安全连接里设置了一个带有secure属性的Cookie,当Cookie被发送到客户端时,它仍然能通过中间人攻击来拦截。

  7. HttpOnly: Cookie的HttpOnly属性,指示浏览器不要在除HTTP和HTTPS请求之外暴露Cookie。

    一个有HttpOnly属性的Cookie,不能通过非HTTP方式来访问,例如通过调用JavaScript(例如,引用 document.cookie)。

    因此,不可能通过跨域脚本(一种非常普通的攻击技术)来偷走这种Cookie。尤其是Facebook 和 Google 正在广泛地使用HttpOnly属性。

如何设置cookie

1. 如何在前端设置cookie
// 设置最简单的cookie,其他属性为默认
document.cookie = "hobby=book;"

// 设置cookie的过期时间为2个小时之后
let expiresDate = new Date();
expiresDate.setTime(expiresDate.getTime() + (1000 * 60 * 60 * 2))
document.cookie = `hobby1=book; expires=${expiresDate.toGMTString()}`;
2. 如何在前端读取cookie

可以直接通过 document.cookie 来获取包含所有cookie字符串,比如:"name=Allen; height=175; gender=male"。也可以通过类似以下函数处理cookie,返回一个对象:

function getCookieObj() {
    let cookieArray = document.cookie.trim().split(";");
    let equalIndex = 0;
    let cookieObj = {};

    cookieArray.forEach(cookie => {
        if (cookie.trim().split("=").shift()) {
            equalIndex = cookie.trim().indexOf("=");
            cookieObj[cookie.trim().split("=").shift()] = cookie.trim().substring(equalIndex + 1);
        }
    });
    
    return cookieObj;
}

console.log(getCookieObj());
/*
{
    gender: "male",
    height: "175",
    name: "Allen"
}
*/
3. 如何在后端设置cookie

无论你的项目后端使用的是什么语言,只要相关API返回的HTTP响应头中包含如下信息,就认为后端要求设置一个cookie,前端接收到这个响应之后,就会在浏览器中保存这个cookie:

Set-cookie: name=name; expires=date; path=path; domain=domain

以Node.js为例,我们可以使用如下代码进行设置cookie:

// 设置基础的cookie
res.writeHead(200, {
    'Set-Cookie': 'myCookie=test',
    'Content-Type': 'text/plain'
});

// 设置多个cookie
res.writeHead(200, {
    'Set-Cookie': ["aaa=bbb","ccc=ddd","eee=fff"],
    'Content-Type': 'text/plain'
});

// 设置包含多个属性的cookie
res.writeHead(200, {
    'Set-Cookie': 'myCookie=test; Expires=Wed, 13-Jan-2021 22:23:01 GMT;HttpOnly ',
    'Content-Type': 'text/html'
});

这是使用原声Node.js进行设置cookie,如果你们项目中使用了Express.js框架,那么设置cookie将会更方便:

// 可以直接使用Express.js提供的api进行设置cookie
function(req, res, next){
    ...
    res.cookie(name, value [, options]);
    res.cookie('user',1,{ expires: new Date(Date.now() + 100), httpOnly: true });
    ...
}

4. 如何在后端读取cookie

当前端往后端发送请求的时候,cookie会被自动放置在HTTP请求头中,所以我们在后端可以直接在请求中进行获取,下面还是以Node.js为例:

http.createServer(function (req, res) {
	...
	console.log(req.headers.cookie);    // 获得客户端的Cookie
	...
}).listen(8000);

相应的,如果你使用了Express.js框架,那么获取cookie将会更加方便:

var express=require('express');
var cookie=require('cookie-parser'); 
var app=express();

app.use(cookie());	// 使用cookie-parser中间件

app.get('/',function(req,res){
	...
    console.log(req.cookies);	// req.cookies是一个cookie对象,可以直接通过键值对的形式获取cookie
    ...
});

自己使用cookie的经验

结合自己的开发经历,这里总结出一些自己的经验:

  1. 不要将一些敏感信息放在cookie里面,例如记录用户信息的jwt
  2. 尽量减少在前端设置cookie,尽可能通过后端API记性设置,并且将Secure和HttPOnly属性设置为true,这样可以防止XSS攻击。
  3. 对于一些需要保存在cookie里面的数据,注意设置cookie的过期时间。