Cookie 與 Session
在我們?cè)L問頁面時(shí),通常是通過 HTTP 協(xié)議的,該協(xié)議的特點(diǎn)之一是無狀態(tài)。當(dāng)我們通過HTTP協(xié)議去訪問某一個(gè)網(wǎng)站,此時(shí)就會(huì)向網(wǎng)站服務(wù)端發(fā)送請(qǐng)求。HTTP 協(xié)議確保每一個(gè)請(qǐng)求都是獨(dú)立的,于是服務(wù)端無法確定當(dāng)前請(qǐng)求的身份,而 Cookie 和 Session可以解決這個(gè)問題。
1.1 Cookie
Cookie 存儲(chǔ)在客戶端,比如訪問網(wǎng)站時(shí)的瀏覽器,每一次請(qǐng)求,Cookie都會(huì)自動(dòng)的發(fā)送給服務(wù)端,當(dāng)然這是可以手動(dòng)禁用的。
Cookie 主要的屬性
key=value: 鍵值對(duì),必須都為字符串類型。值為 Unicode字符時(shí),使用字符編碼。值為二進(jìn)制數(shù)據(jù)時(shí),使用 Base64編碼
domain: 指定 cookie 所屬的域名,默認(rèn)是當(dāng)前域名
path: 指定 cookie 在哪個(gè)路徑(即路由)下生效,默認(rèn)為 ‘/’。若指定為/user,那么只有 /user 下的路由可以訪問到該 cookie,如:/user/find
maxAge: cookie 失效時(shí)間,單位:秒, maxAge < 0 則關(guān)閉瀏覽器就失效,maxAge = 0 ,刪除,默認(rèn)情況maxAge = -1
expires: cookie 過期時(shí)間,在設(shè)置的某個(gè)時(shí)間后 該 cookie失效。沒有 maxAge 好用。
secure: true / false,表示 cookie 是否使用安全協(xié)議(如 HTTPS,SSL)傳輸,默認(rèn)為 false。設(shè)置為 ture 時(shí), cookie 在HTTP傳輸中無效,而在 HTTPS 中會(huì)有效。
httpOnly: 禁止通過 JS腳本獲取 cookie,但可以通過瀏覽器調(diào)試工具獲取到 Application 里的 cookie,能防一定的XSS攻擊
1.2 Session
Session 是另一種記錄服務(wù)端和客戶端會(huì)話狀態(tài)的機(jī)制,Session 是基于 Cookie 實(shí)現(xiàn)的,Session 存儲(chǔ)在服務(wù)端,當(dāng)發(fā)送請(qǐng)求后,服務(wù)端會(huì)存儲(chǔ)當(dāng)前請(qǐng)求的sessionId (區(qū)分不同的客戶端)到 客戶端的 Cookie 中。
安全性: Cookie?不安全,存儲(chǔ)在客戶端,可自行修改
????????????????Session?安全,存儲(chǔ)在服務(wù)端
存儲(chǔ)類型 :Cookie?key為字符串value也為字符串
????????????????Session?key為字符串,value 可為能序列化的任何數(shù)據(jù)
有效期 :Cookie?長(zhǎng)時(shí)間保持
????????????????Session?時(shí)間較短,默認(rèn)為30秒,且在客戶端關(guān)閉就失效。
存儲(chǔ)量 :單個(gè)Cookie不能超過4K,即4096 Byte
????????????????Session?沒有固定限制,存儲(chǔ)在內(nèi)存,但是過大會(huì)占資源。
2.1 Cookie
使用 Cookie 需注意的問題:
存儲(chǔ)在客戶端,容易被篡改,使用前需驗(yàn)證是否合法
盡量不存敏感數(shù)據(jù),如用戶密碼,賬號(hào)余額
使用 httpOnly ,提高安全性
存儲(chǔ)的數(shù)據(jù)量不能過大,單個(gè)Cookie最多只能存儲(chǔ) 4kb 的數(shù)據(jù)
設(shè)置正確的 domain 域 和 path,盡量減少數(shù)據(jù)傳輸
無法跨域
瀏覽器對(duì)單個(gè)網(wǎng)站最多存 20個(gè) Cookie,而瀏覽器一般指允許存放 300個(gè)Cookie
移動(dòng)端不適合用 Cookie ,而 Session 又是基于 Cookie 實(shí)現(xiàn),故移動(dòng)端常用 Token
使用 Cookie 存儲(chǔ),需要設(shè)置有效時(shí)間,這會(huì)影響 JWT 的時(shí)間設(shè)置,比如當(dāng) JWT 有效時(shí)間為 1天,那么 Cookie 的有效時(shí)間必須大于或等于 1天。
2.2 Session
使用 Session 需注意的問題:
當(dāng)用戶在線過多時(shí),會(huì)占據(jù)更多內(nèi)存,需在服務(wù)端定期處理過期的 Session
集群環(huán)境下,服務(wù)端需解決多臺(tái) web 服務(wù)器共享session的問題,否則同一個(gè)用戶無法訪問服務(wù)端不同的web服務(wù)器。
共享session 會(huì)遇到跨域問題,服務(wù)端需在各個(gè)應(yīng)用間解決 cookie 跨域 問題
sessionId 存儲(chǔ)在客戶端的 cookie中,如果瀏覽器禁止 cookie 或不支持 cookie,可以把 sessionId 寫在 URL 參數(shù)后面,即 session不一定必須基于 cookie 實(shí)現(xiàn)
2.3 Token
使用 Token 需注意的問題:
數(shù)據(jù)庫存儲(chǔ) Token,若導(dǎo)致查詢時(shí)間長(zhǎng),可選擇存放在內(nèi)存,比如使用 Redis 數(shù)據(jù)庫
Token 由應(yīng)用管理,避免了同源策略的限制。
Token 不依賴 Cookie, 可避免 CSRF(跨站域請(qǐng)求偽造)攻擊
2.4 JWT
使用 JWT 需注意的問題:
JWT 不依賴 Cookie,,支持 CORS 跨域資源共享
JWT 默認(rèn)不加密,不過可設(shè)置加密,生成原始Toekn后,再用指定的加密算法加密一次
JWT 不加密時(shí)不能存放敏感數(shù)據(jù),否則有安全隱患
JWT 除了認(rèn)證以外,還可以用來交換信息,降低了服務(wù)端的數(shù)據(jù)庫交互
JWT 最大優(yōu)勢(shì)是服務(wù)端無需依賴 Session,方便服務(wù)端的認(rèn)證與授權(quán)業(yè)務(wù)的拓展,同時(shí)也有一定局限,無法在使用過程中廢棄某個(gè) Token 或 更改 Token 的權(quán)限,除非使用更多的邏輯進(jìn)行處理
JWT 本身包含了認(rèn)證信息,故最好將JWT有效期設(shè)置短一些,對(duì)于比較重要的權(quán)限,使用時(shí)應(yīng)再次驗(yàn)證用戶身份
JWT 適合一次性的命令認(rèn)證,頒發(fā)有效期很短的JWT,降低泄露的危險(xiǎn)
為了降低盜用概率,JWT 應(yīng)使用 HTTPS 安全協(xié)議傳輸。



JWT 認(rèn)證流程:
用戶攜帶個(gè)人的用戶名和密碼發(fā)送請(qǐng)求,服務(wù)端認(rèn)證成功后,返回客戶端一個(gè)JWT字符串
客戶端將 Token 保存到本地(瀏覽器的話通常是 localStorage 或 Cookie)
當(dāng)用戶要訪問受保護(hù)的路由或資源時(shí),需設(shè)置 Header 請(qǐng)求頭的 Authorization 的值,使用 Bearer 模式 添加 JWT 字符串,比如 Authorization: Bearer <token>
服務(wù)端的保護(hù)路由會(huì)檢查 Header 種 Authorization 中的 JWT 信息,若合法則運(yùn)行用戶行為。
使用 JWT 的特點(diǎn):
內(nèi)部包含一些會(huì)話信息,除了驗(yàn)證、簽名生成 JWT外,其他情況無需數(shù)據(jù)庫交互
JWT 并不一定依賴于 Cookie,所以可以跨域訪問,即支持向第三方應(yīng)用進(jìn)行認(rèn)證與授權(quán)
由于用戶狀態(tài)并不存儲(chǔ)在服務(wù)端的內(nèi)存,故是一種無狀態(tài)的認(rèn)證機(jī)制
3.2 JWT 與 Token 相比
JWT 是 Tokne 的拓展,它們之間的 相同點(diǎn) 有:
都是訪問資源的令牌
都可以記錄用戶信息
都使服務(wù)端無狀態(tài)化
都必須通過驗(yàn)證,客戶端才能訪問服務(wù)端受保護(hù)的資源
不同點(diǎn):
Token,服務(wù)端 必須 查詢數(shù)據(jù)庫獲取用戶信息,然后再驗(yàn)證 Token 是否有效,
JWT 是將 Token 和 Payload 加密存儲(chǔ)在客戶端,服務(wù)端只需使用密鑰解密進(jìn)行驗(yàn)證,無需查詢數(shù)據(jù)庫
3.3 JWT的主要組成
JWT 主要由三部分組成,分別是 header(請(qǐng)求頭)、payload(有效負(fù)載)、signature(簽名),JWT由這三個(gè)字符串以句點(diǎn) . 的方式連接,JWT 通常顯示為:xxx.yyy.zzz,即 Header.Payload.Signature
3.3.1 Header
Header 標(biāo)頭通常由兩部分組成:
令牌的類型(即 JWT )
簽名算法
如 HMAC SHA256 或 RSA,它會(huì)用 Base64 編碼組成 JWT 結(jié)構(gòu)的第一部分
注:Base64 是一種編碼,即是可以進(jìn)行解碼的,并不是一種加密過程。
3.3.2 Payload
令牌的第二部分是 Payload (有效負(fù)載),主要存儲(chǔ)一些鍵值對(duì)
和 Header 部分一樣,Payload 會(huì)采用 Base64 編碼,不過是作為 JWT 結(jié)構(gòu)的第二部分
3.3.3 Signature
前面兩部分都是用 Base64 進(jìn)行編碼的,即前端后端都可以解析里面的信息,Signature 簽名需要使用編碼后的header 和 payload 以及我們提供的一個(gè)字符串密鑰,然后使用 header 中指定的簽名算法,例如 HS256 進(jìn)行簽名。簽名的作用是保證 JWT 沒有被篡改過。
Base64 是一種編碼方式,是可逆的,那么信息就容易暴露。在JWT中,不應(yīng)該在 payload 負(fù)載里加入任何?敏感?的數(shù)據(jù),例如用戶的個(gè)人密碼,不適合放到 JWT。
JWT 經(jīng)常用于設(shè)計(jì)用戶認(rèn)證和授權(quán)系統(tǒng),或者實(shí)現(xiàn) Web 應(yīng)用的單點(diǎn)登錄。
Header、Payload 和 Signature,前兩個(gè)是基于 Base64 編碼的,而簽名 SIgnature 則是由服務(wù)端來決定,通過【加密算法】 + 【密鑰】規(guī)定一種加密機(jī)制,從而確定簽名。
簽名的密鑰是固定的,只要不泄露,密碼就不會(huì)出現(xiàn)問題,當(dāng)然可以設(shè)置的更復(fù)雜一些,比如拿用戶的用戶名進(jìn)行加密,然后再次使用加密算法進(jìn)行簽名,即二次加密,最后將加密后的用戶名存儲(chǔ)到 Payload里,這樣一來每次 JWT 驗(yàn)證,既不用查詢數(shù)據(jù)庫,也不用擔(dān)心各用戶的簽名都一樣。