何爲 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 也是一樣的道理,這裏不去細說。