摘要:由于是存在客戶端上的,所以瀏覽器加入了一些限制確保不會被惡意使用,同時不會占據(jù)太多磁盤空間。簽名是對前兩部分的簽名,防止數(shù)據(jù)被篡改。的作用最開始的初衷是為了實現(xiàn)授權(quán)和身份認證作用的,可以實現(xiàn)無狀態(tài),分布式的應(yīng)用授權(quán)。
前言 無狀態(tài)的HTTP協(xié)議
很久很久之前, Web基本都是文檔的瀏覽而已。既然是瀏覽, 作為服務(wù)器, 不需要記錄在某一段時間里都瀏覽了什么文檔, 每次請求都是一個新的HTTP協(xié)議,就是請求加響應(yīng)。不用記錄誰剛剛發(fā)了HTTP請求, 每次請求都是全新的
如何管理會話隨著交互式Web應(yīng)用的興起, 像在線購物網(wǎng)站,需要登錄的網(wǎng)站等,馬上面臨一個問題,就是要管理回話,記住那些人登錄過系統(tǒng),哪些人往自己的購物車中放商品,也就是說我必須把每個人區(qū)分開。
本文主要講解cookie,session, token 這三種是如何管理會話的;
cookiecookie 是一個非常具體的東西,指的就是瀏覽器里面能永久存儲的一種數(shù)據(jù)。跟服務(wù)器沒啥關(guān)系,僅僅是瀏覽器實現(xiàn)的一種數(shù)據(jù)存儲功能。
cookie由服務(wù)器生成,發(fā)送給瀏覽器,瀏覽器把cookie以KV形式存儲到某個目錄下的文本文件中,下一次請求同一網(wǎng)站時會把該cookie發(fā)送給服務(wù)器。由于cookie是存在客戶端上的,所以瀏覽器加入了一些限制確保cookie不會被惡意使用,同時不會占據(jù)太多磁盤空間。所以每個域的cookie數(shù)量是有限制的。
如何設(shè)置 客戶端設(shè)置document.cookie = "name=xiaoming; age=12 "
客戶端可以設(shè)置cookie的一下選項: expires, domain, path, secure(只有在https協(xié)議的網(wǎng)頁中, 客戶端設(shè)置secure類型cookie才能生效), 但無法設(shè)置httpOnly選項
設(shè)置cookie => cookie被自動添加到request header中 => 服務(wù)端接收到cookie服務(wù)端設(shè)置
不管你是請求一個資源文件(如html/js/css/圖片), 還是發(fā)送一個ajax請求, 服務(wù)端都會返回response.而response header中有一項叫set-cookie, 是服務(wù)端專門用來設(shè)置cookie的;
一個set-cookie只能設(shè)置一個cookie, 當你想設(shè)置多個, 需要添加同樣多的set-cookie
服務(wù)端可以設(shè)置cookie的所有選項: expires, domain, path, secure, HttpOnly
Cookie,SessionStorage,LocalStorageHTML5提供了兩種本地存儲的方式 sessionStorage 和 localStorage;
session從字面上講,就是會話。這個就類似你和一個人交談,你怎么知道當時和你交談的是張三而不是李四呢?對方肯定有某種特征(長相等)表明他是張三;
session也是類似的道理,服務(wù)器要知道當前請求發(fā)給自己的是誰。為了做這種區(qū)分,服務(wù)器就是要給每個客戶端分配不同的"身份標識",然后客戶端每次向服務(wù)器發(fā)請求的時候,都帶上這個”身份標識“,服務(wù)器就知道這個請求來自與誰了。
至于客戶端怎么保存這個”身份標識“,可以有很多方式,對于瀏覽器客戶端,大家都采用cookie的方式。
1.用戶向服務(wù)器發(fā)送用戶名和密碼
2.服務(wù)器驗證通過后,在當前對話(session)里面保存相關(guān)數(shù)據(jù),比如用戶角色, 登陸時間等;
3.服務(wù)器向用戶返回一個session_id, 寫入用戶的cookie
4.用戶隨后的每一次請求, 都會通過cookie, 將session_id傳回服務(wù)器
5.服務(wù)端收到 session_id, 找到前期保存的數(shù)據(jù), 由此得知用戶的身份
存在的問題 擴展性不好單機當然沒問題, 如果是服務(wù)器集群, 或者是跨域的服務(wù)導(dǎo)向架構(gòu), 這就要求session數(shù)據(jù)共享,每臺服務(wù)器都能夠讀取session。
舉例來說, A網(wǎng)站和B網(wǎng)站是同一家公司的關(guān)聯(lián)服務(wù)。現(xiàn)在要求,用戶只要在其中一個網(wǎng)站登錄,再訪問另一個網(wǎng)站就會自動登錄,請問怎么實現(xiàn)?這個問題就是如何實現(xiàn)單點登錄的問題
Nginx ip_hash 策略,服務(wù)端使用 Nginx 代理,每個請求按訪問 IP 的 hash 分配,這樣來自同一 IP 固定訪問一個后臺服務(wù)器,避免了在服務(wù)器 A 創(chuàng)建 Session,第二次分發(fā)到服務(wù)器 B 的現(xiàn)象。
Session復(fù)制:任何一個服務(wù)器上的 Session 發(fā)生改變(增刪改),該節(jié)點會把這個 Session 的所有內(nèi)容序列化,然后廣播給所有其它節(jié)點。
共享Session:將Session Id 集中存儲到一個地方,所有的機器都來訪問這個地方的數(shù)據(jù)。這種方案的優(yōu)點是架構(gòu)清晰,缺點是工程量比較大。另外,持久層萬一掛了,就會單點失敗;
另一種方案是服務(wù)器索性不保存session數(shù)據(jù)了,所有數(shù)據(jù)就保存在客戶端,每次請求都發(fā)回服務(wù)器。這種方案就是接下來要介紹的基于Token的驗證;
Token 過程用戶通過用戶名和密碼發(fā)送請求
程序驗證
程序返回一個簽名的token給客戶端
客戶端儲存token, 并且每次用每次發(fā)送請求
服務(wù)端驗證Token并返回數(shù)據(jù)
這個方式的技術(shù)其實很早就已經(jīng)有很多實現(xiàn)了,而且還有現(xiàn)成的標準可用,這個標準就是JWT;
JWT(JSON Web Token) 數(shù)據(jù)結(jié)構(gòu)實際的JWT大概就像下面這樣:
JSON Web Tokens由dot(.)分隔的三個部分組成,它們是:
Header(頭部)
Payload(負載)
Signature(簽名)
因此,JWT通常如下展示:
xxxxx.yyyyy.zzzz
Header(頭部)Header 是一個 JSON 對象
{ "alg": "HS256", // 表示簽名的算法,默認是 HMAC SHA256(寫成 HS256) "typ": "JWT" // 表示Token的類型,JWT 令牌統(tǒng)一寫為JWT }Payload(負載)
Payload 部分也是一個 JSON 對象,用來存放實際需要傳遞的數(shù)據(jù)
{ // 7個官方字段 "iss": "a.com", // issuer:簽發(fā)人 "exp": "1d", // expiration time: 過期時間 "sub": "test", // subject: 主題 "aud": "xxx", // audience: 受眾 "nbf": "xxx", // Not Before:生效時間 "iat": "xxx", // Issued At: 簽發(fā)時間 "jti": "1111", // JWT ID:編號 // 可以定義私有字段 "name": "John Doe", "admin": true }
JWT 默認是不加密的,任何人都可以讀到,所以不要把秘密信息放在這個部分。
Signature(簽名)Signature 是對前兩部分的簽名,防止數(shù)據(jù)被篡改。
首先,需要指定一個密鑰(secret)。這個密鑰只有服務(wù)器才知道,不能泄露給用戶。然后,使用Header里面指定的簽名算法(默認是 HMAC SHA256),按照下面的公式產(chǎn)生簽名。
HMACSHA256(base64UrlEncode(header) + "." + base64UrlEncode(payload), secret)
算出簽名后,把 Header、Payload、Signature 三個部分拼成一個字符串,每個部分之間用"點"(.)分隔,就可以返回給用戶。
JWT = Base64(Header) + "." + Base64(Payload) + "." + $Signature
如何保證安全?
發(fā)送JWT要使用HTTPS;不使用HTTPS發(fā)送的時候,JWT里不要寫入秘密數(shù)據(jù)
JWT的payload中要設(shè)置expire時間
使用方式客戶端收到服務(wù)器返回的 JWT,可以儲存在 Cookie 里面,也可以儲存在 localStorage。此后,客戶端每次與服務(wù)端通信,都要帶上這個JWT。你可以把它放在Cookie里面自動發(fā)送,但是這樣不能跨域,所以更好的做法是放在HTTP請求的頭信息 Authorization 字段里面。
Authorization: Bearer
另一種做法是, 跨域的時候, JWT就放在POST請求的數(shù)據(jù)體里。
JWT 的作用JWT最開始的初衷是為了實現(xiàn)授權(quán)和身份認證作用的,可以實現(xiàn)無狀態(tài),分布式的Web應(yīng)用授權(quán)。大致實現(xiàn)的流程如下
客戶端需要攜帶用戶名/密碼等可證明身份的的內(nèi)容去授權(quán)服務(wù)器獲取JWT信息;
每次服務(wù)都攜帶該Token內(nèi)容與Web服務(wù)器進行交互,由業(yè)務(wù)服務(wù)器來驗證Token是否是授權(quán)發(fā)放的有效Token,來驗證當前業(yè)務(wù)是否請求合法。
這里需要注意:不是每次請求都要申請一次Token,這是需要注意,如果不是對于安全性要求的情況,不建議每次都申請,因為會增加業(yè)務(wù)耗時;比如只在登陸時申請,然后使用JWT的過期時間或其他手段來保證JWT的有效性;
一個簡單的JWT使用示例 準備npm i --save koa koa-route koa-bodyparser @koa/cors jwt-simple服務(wù)端代碼
const Koa = require("koa"); const app = new Koa(); const route = require("koa-route"); var bodyParser = require("koa-bodyparser"); const jwt = require("jwt-simple"); const cors = require("@koa/cors"); const secret = "your_secret_string"; // 加密用的SECRET字符串,可隨意更改 app.use(bodyParser()); // 處理post請求的參數(shù) const login = ctx => { const req = ctx.request.body; const userName = req.userName; const expires = Date.now() + 3600000; // 設(shè)置超時時間為一小時后 var payload = { iss: userName, exp: expires }; const Token = jwt.encode(payload, secret); ctx.response.body = { data: Token, msg: "登陸成功" }; } const getUserName = ctx => { const reqHeader = ctx.request.headers; const token = reqHeader.authorization.split(" ")[1]; var decoded = jwt.decode(token, secret); ctx.response.body = { data: { username: decoded.iss, }, msg: "獲取用戶名成功" }; } app.use(cors()); app.use(route.post("/login", login)); app.use(route.get("/getUsername", getUserName)); app.listen(3200, () => { console.log("啟動成功"); });客戶端代碼
運行代碼JWT-demo
以上只是一個特別簡單的例子, 很多邊界條件沒有做處理,比如未登陸的校驗,異常的處理,Token過期的判斷;區(qū)別 Cookie和Session的區(qū)別
存儲位置不同: cookie數(shù)據(jù)存放在客戶的瀏覽器上,session數(shù)據(jù)放在服務(wù)器上
隱私策略不同:cookie不是很安全, 別人可以分析存放在本地的cookie并進行cookie欺騙,考慮到安全應(yīng)當使用session
session會在一定時間內(nèi)保存在服務(wù)器上。當訪問增多,就會比較占用你服務(wù)器的性能,考慮到減輕服務(wù)器性能方面,應(yīng)當使用cookie
存儲大小不同: 單個cookie保存的數(shù)據(jù)不能超過4k, 很多瀏覽器都限制一個站點最多保存20個cookie
一般建議: 將登陸信息等重要信息存放為session, 其他信息如果需要保留,可以放在cookie中Token和Session的區(qū)別
Session是一種HTTP儲存機制, 為無狀態(tài)的HTTP提供持久機制;
Token就是令牌, 比如你授權(quán)(登錄)一個程序時,它就是個依據(jù),判斷你是否已經(jīng)授權(quán)該軟件;
Session和Token并不矛盾,作為身份認證Token安全性比Session好,因為每一個請求都有簽名還能防止監(jiān)聽以及重放攻擊,而Session就必須依賴鏈路層來保障通訊安全了。如上所說,如果你需要實現(xiàn)有狀態(tài)的回話,仍然可以增加Session來在服務(wù)端保存一些狀態(tài)。
總結(jié)cookie,session,Token沒有絕對的好與壞之分,只要還是要結(jié)合實際的業(yè)務(wù)場景和需求來決定采用哪種方式來管理回話,當然也可以三種都用。
參考jwt
徹底理解cookie,session,token
JSON Web Token 入門教程
Cookie、Session、Token那點事兒(原創(chuàng))
3種web會話管理的方式
你真的了解 Cookie 和 Session 嗎
不要用JWT替代session管理(上):全面了解Token,JWT,OAuth,SAML,SSO
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://www.ezyhdfw.cn/yun/104652.html
摘要:此時,用戶想要訪問系統(tǒng)受限的資源比如說訂單功能,訂單功能需要登錄后才能訪問,系統(tǒng)發(fā)現(xiàn)用戶并沒有登錄,于是重定向到認證中心,并將自己的地址作為參數(shù)。 前言 只有光頭才能變強。文本已收錄至我的GitHub倉庫,歡迎Star:https://github.com/ZhongFuCheng3y/3y 在我實習之前我就已經(jīng)在看單點登錄的是什么了,但是實習的時候一直在忙其他的事,所以有幾個網(wǎng)站就...
摘要:此時,用戶想要訪問系統(tǒng)受限的資源比如說訂單功能,訂單功能需要登錄后才能訪問,系統(tǒng)發(fā)現(xiàn)用戶并沒有登錄,于是重定向到認證中心,并將自己的地址作為參數(shù)。前言 只有光頭才能變強。 文本已收錄至我的GitHub倉庫,歡迎Star:github.com/ZhongFuChen… 在我實習之前我就已經(jīng)在看單點登錄的是什么了,但是實習的時候一直在忙其他的事,所以有幾個網(wǎng)站就一直躺在我的收藏夾里邊: ...
摘要:此時,用戶想要訪問系統(tǒng)受限的資源比如說訂單功能,訂單功能需要登錄后才能訪問,系統(tǒng)發(fā)現(xiàn)用戶并沒有登錄,于是重定向到認證中心,并將自己的地址作為參數(shù)。前言 只有光頭才能變強。 文本已收錄至我的GitHub倉庫,歡迎Star:github.com/ZhongFuChen… 在我實習之前我就已經(jīng)在看單點登錄的是什么了,但是實習的時候一直在忙其他的事,所以有幾個網(wǎng)站就一直躺在我的收藏夾里邊: ...
閱讀 3577·2021-11-23 10:13
閱讀 922·2021-09-22 16:01
閱讀 959·2021-09-09 09:33
閱讀 734·2021-08-05 09:58
閱讀 1776·2019-08-30 11:14
閱讀 2086·2019-08-30 11:02
閱讀 3328·2019-08-29 16:28
閱讀 1545·2019-08-29 16:09