何为 cookie 呢?咱们在上面了解到 HTTP 是无状态的,但随着 Web 的不断发展,这种 无状态 的特性出现了弊端。当你登陆到一家购物网站,在跳转到该站的其余页面时也应该继续保持登陆状态。可是由于 HTTP 是无状态的,因此必须得在浏览器端存储一些信息来标识当前用户,所以 cookie 应运而生,它一种浏览器管理状态的文件。html
浏览器第一次发出请求,服务器会将 cookie 放入到响应请求中,在浏览器第二次发请求的时候,会把 cookie 带过去,因而服务端就会辨别用户身份。注意:单个 cookie 保存的数据不能超过 4K,不少浏览器都限制一个站点最多保存 20 个 cookie。前端
cookie 在请求头中有一个 cookie 字段,在响应头里有一个 set-cookie 字段。web
cookie 自己就是用来保存一些隐私性的字段,基于安全性的考量,必需要保证它是 不可跨域的。咱们能够作个实验:先打开 google.com,而后在开发者工具中输入如下代码:express
document.cookie = 'hello=world;path=/;domain=.baidu.com';
document.cookie = 'world=hello;path=/;domain=.google.com';
复制代码
打开 Application 选项卡,在侧边栏找到 Cookies,能够发现只有 domain 为 .google.com 的被成功添加。json
咱们经过一个登陆的小例子来了解服务端设置 cookie。首先经过 express application generator 生成一个 Express 工程。后端
接着在 index.html 文件中输入如下代码,咱们建立一个输入用户名和密码的界面,在点击按钮的时候,经过 fetch 将输入的值发送给后端。跨域
<fieldset>
<legend>Login</legend>
<input id="userName" type="text" placeholder="请输入用户名" />
<input id="userPwd" type="password" placeholder="请输入密码" />
<button id="loginBtn">登陆</button>
</fieldset>
<p>登陆状态: <span id="result"></span></p>
<script>
const userName = document.getElementById('userName');
const userPwd = document.getElementById('userPwd');
const loginBtn = document.getElementById('loginBtn');
const result = document.getElementById('result');
loginBtn.addEventListener('click', function() {
const data = {
userName: userName.value,
userPwd: userPwd.value
};
fetch('/login', {
method: 'POST',
headers: new Headers({
'Content-Type': 'application/json'
}),
body: JSON.stringify(data)
})
.then(res => {
return res.json();
})
.then(json => {
result.innerHTML = json.msg;
});
});
</script>
复制代码
当用户名和密码匹配时 (假设用户名和密码都是 yancey),返回给客户端一个 cookie 以及登陆成功的 json;不然返回登陆失败的 json。下面是模拟服务端登陆的接口。浏览器
router.post('/login', (req, res, next) => {
const body = req.body;
if (body.userName === 'yancey' && body.userPwd === 'yancey') {
// 设置 cookie
res.cookie('yancey', 'success');
res.json({
success: true,
msg: '登陆成功'
});
} else {
res.status(401).json({
success: false,
msg: '用户名或密码错误'
});
}
});
复制代码
经过这个例子能够看到,在 express 中,setCookie 的方式为:第一个参数传递 name,第二个参数传递 value,注意浏览器会将元字符和语义字符以外的字符进行转义。打开 Chrome 的开发者工具,就能够看到该 cookie 被添加到浏览器上了。或者你在控制台输入 document.cookie,一样能够看到 cookie 字符串。安全
这只是一个设置 cookie 的简单例子,cookie 有 7 种属性可供使用,咱们一一来了解。bash
domain
该属性给 cookie 设置 域名,默认为当前网站的域名,下面的例子将 domain 设为 yanceyleo.com,因为前端页面是 127.0.0.1,根据同源策略,该条 cookie 不会生效。
res.cookie('domain', 'domian', { domain: 'yanceyleo.com' });
复制代码
expires / maxAge
httpOnly
当该属性设为 true 时,document.cookie 将没法获取该条 cookie,但服务端能够照常得到。该属性能够有效的避免跨站脚本攻击 (XSS)。 关于几种脚本攻击`
res.cookie('httpOnly', 'httpOnly', {
// 只能被 web server 访问到,也就是说在浏览器输入 document.cookie 没法取到该条 cookie,目的是防止 xss
httpOnly: true
});
复制代码
path
该属性给 指定的路径 添加此 cookie,默认为 /。以下代码就是给 users 这个路由设置 cookie (即使在服务端该路径不存在也会被添加上)。
res.cookie('path', 'path', {
path: '/users'
});
复制代码
secure
只有当链接是 HTTPS 协议,该 cookie 才会被添加。该属性默认为 fasle。由于我本地的 express 是 HTTP 协议,所以该条 cookie 不会生效。
res.cookie('secure', 'secure', {
secure: true
});
复制代码
signed (防篡改签名)
该属性是给浏览器发送一个加密的 cookie,该属性默认为 false。在 express 中,咱们可使用 cookie-parser 插件来建立一个加密后的 cookie。服务端经过该 cookie 的内容和签名来检验它是否 被篡改
首先给 cookieParser 传入一个 secret。
app.use(cookieParser('forcabarca'));
而后返回一个 sign 后的 cookie。
res.cookie('signed', 'signed', {
signed: true
});
复制代码
在 express 中,咱们可使用 req.cookies 来得到 未加密 的 cookie 对象,能够经过 req.signedCookies 来得到 已加密 的 cookie 对象。
console.log(req.cookies); // { httpOnly: 'httpOnly' }
console.log(req.signedCookies); // { signed: 'signed' }
复制代码
ession 是服务端使用的一种记录客户端状态的机制,与 cookie 不一样的是,session 保存在 服务端。当客户端初次发送请求时 (好比登陆成功),服务端会将用户信息以某种形式保存在服务端,当再次访问时只需从该 session 中找到该客户的状态便可。
所以,cookie 机制就是经过检查客户身上的 “通行证” 来肯定客户身份,而 session 则是经过检查服务器上的 “客户明细表” 来确认客户身份。session 至关于程序在服务器上创建的一份客户档案,客户来访的时候只须要查询客户档案表就能够了。
由于 HTTP 是无状态的,因此单纯的 session 仍不能判断是否为究竟是哪一个用户。所以服务端仍要向客户端发送一个 maxAge 为 -1 的 cookie 来做为不一样用户的惟一标识。
固然你也能够不使用 cookie,你能够经过重写 URL 地址的方式来实现。它的原理是将用户的 seesion id 写入到 URL 中,当浏览器解析新的 URL 时就能够定位到是哪位用户。
万变不离其宗,两种方式都是要保证用户信息以某种形式保存到客户端。更先进的 localStorage,sessionStorage,IndexedDB 也是一样的道理,这里不去细说。