cookie,session,token傻傻分不清

  • 2021 年 1 月 30 日
  • 筆記

什麼是認證(Authentication)

• 通俗地講就是驗證當前用戶的身份,證明「你是你自己」(比如:你每天上下班打卡,都需要通過指紋打卡,當你的指紋和系統里錄入的指紋相匹配時,就打卡成功)
• 互聯網中的認證: 
	○ 用戶名密碼登錄
	○ 郵箱發送登錄鏈接
	○ 手機號接收驗證碼
	○ 只要你能收到郵箱/驗證碼,就默認你是帳號的主人

什麼是授權(Authorization)

• 用戶授予第三方應用訪問該用戶某些資源的許可權 
	○ 你在安裝手機應用的時候,APP 會詢問是否允許授予許可權(訪問相冊、地理位置等許可權)
	○ 你在訪問微信小程式時,當登錄時,小程式會詢問是否允許授予許可權(獲取昵稱、頭像、地區、性別等個人資訊)
• 實現授權的方式有:cookie、session、token、auth
   HTTP 是無狀態的協議(對於事務處理沒有記憶能力,每次客戶端和服務端會話完成時,服務端不會保存任何會話資訊):每個請求都是完全獨立的,服務端無法確認當前訪問者的身份資訊,無法分辨      
   上            
   一次的請求發送者和這一次的發送者是不是同一個人。所以伺服器與瀏覽器為了進行會話跟蹤(知道是誰在訪問我),就必須主動的去維護一個狀態,這個狀態用於告知服務端前後兩個請求是否來自 
   同一瀏覽器。而這個狀態需要通過 cookie 或者 session 去實現。
    cookie 存儲在客戶端: cookie 是伺服器發送到用戶瀏覽器並保存在本地的一小塊數據,它會在瀏覽器下次向同一伺服器再發起請求時被攜帶並發送到伺服器上。
    cookie 是不可跨域的: 每個 cookie 都會綁定單一的域名,無法在別的域名下獲取使用,一級域名和二級域名之間是允許共享使用的(靠的是 domain)。
屬性	說明
name=value                   	   鍵值對,設置 Cookie 的名稱及相對應的值,都必須是字元串類型
 	  - 如果值為 Unicode 字元,需要為字元編碼。
  	 - 如果值為二進位數據,則需要使用 BASE64 編碼。
domain	指定 cookie 所屬域名,默認是當前域名
path	指定 cookie 在哪個路徑(路由)下生效,默認是 '/'。
	如果設置為 /abc,則只有 /abc 下的路由可以訪問到該 cookie,如:/abc/read。
maxAge	cookie 失效的時間,單位秒。如果為整數,則該 cookie 在 maxAge 秒後失效。如果為負數,該 cookie 為臨時 cookie ,關閉瀏覽器即失效,瀏覽器也不會以任何形式保存該 
                  cookie 。如果為 0,表示刪除該 cookie 。默認為 -1。
	- 比 expires 好用。
expires	過期時間,在設置的某個時間點後該 cookie 就會失效。
	一般瀏覽器的 cookie 都是默認儲存的,當關閉瀏覽器結束這個會話的時候,這個 cookie 也就會被刪除
secure	該 cookie 是否僅被使用安全協議傳輸。安全協議有 HTTPS,SSL等,在網路上傳輸數據之前先將數據加密。默認為false。
	當 secure 值為 true 時,cookie 在 HTTP 中是無效,在 HTTPS 中才有效。
httpOnly	如果給某個 cookie 設置了 httpOnly 屬性,則無法通過 JS 腳本 讀取到該 cookie 的資訊,但還是能通過 Application 中手動修改 cookie,所以只是在一定程度上可以 
                   防止 XSS 攻擊,不是絕對的安全

什麼是 Session

	○ session 是另一種記錄伺服器和客戶端會話狀態的機制
	○ session 是基於 cookie 實現的,session 存儲在伺服器端,sessionid 會被存儲到客戶端的cookie 中

		§ session 認證流程: 
		§ 用戶第一次請求伺服器的時候,伺服器根據用戶提交的相關資訊,創建對應的 Session
		§ 請求返回時將此 Session 的唯一標識資訊 SessionID 返回給瀏覽器
		§ 瀏覽器接收到伺服器返回的 SessionID 資訊後,會將此資訊存入到 Cookie 中,同時 Cookie 記錄此 SessionID 屬於哪個域名
		§ 當用戶第二次訪問伺服器的時候,請求會自動判斷此域名下是否存在 Cookie 資訊,如果存在自動將 Cookie 資訊也發送給服務端,服務端會從 Cookie 中獲取 
                     SessionID,再根據 SessionID 查找對應的 Session 資訊,如果沒有找到說明用戶沒有登錄或者登錄失效,如果找到 Session 證明用戶已經登錄可執行後面操作。
                  根據以上流程可知,SessionID 是連接 Cookie 和 Session 的一道橋樑,大部分系統也是根據此原理來驗證用戶登錄狀態。
	○ 安全性: Session 比 Cookie 安全,Session 是存儲在伺服器端的,Cookie 是存儲在客戶端的。
	○ 存取值的類型不同:Cookie 只支援存字元串數據,想要設置其他類型的數據,需要將其轉換成字元串,Session 可以存任意數據類型。
	○ 有效期不同: Cookie 可設置為長時間保持,比如我們經常使用的默認登錄功能,Session 一般失效時間較短,客戶端關閉(默認情況下)或者 Session 超時都會失效。
	○ 存儲大小不同: 單個 Cookie 保存的數據不能超過 4K,Session 可存儲數據遠高於 Cookie,但是當訪問量過多,會佔用過多的伺服器資源。

什麼是 Token(令牌)

Acesss Token
	○ 訪問資源介面(API)時所需要的資源憑證
	○ 簡單 token 的組成: uid(用戶唯一的身份標識)、time(當前時間的時間戳)、sign(簽名,token 的前幾位以哈希演算法壓縮成的一定長度的十六進位字元串)
	○ 特點: 
	○ 服務端無狀態化、可擴展性好
	○ 支援移動端設備
	○ 安全
	○ 支援跨程式調用
	○ token 的身份驗證流程:

			1) 客戶端使用用戶名跟密碼請求登錄
			2) 服務端收到請求,去驗證用戶名與密碼
			3) 驗證成功後,服務端會簽發一個 token 並把這個 token 發送給客戶端
			4) 客戶端收到 token 以後,會把它存儲起來,比如放在 cookie 里或者 localStorage 里
			5) 客戶端每次向服務端請求資源的時候需要帶著服務端簽發的 token
			6) 服務端收到請求,然後去驗證客戶端請求裡面帶著的 token ,如果驗證成功,就向客戶端返回請求的數據
	○ 每一次請求都需要攜帶 token,需要把 token 放到 HTTP 的 Header 里
	○ 基於 token 的用戶認證是一種服務端無狀態的認證方式,服務端不用存放 token 數據。用解析 token 的計算時間換取 session 的存儲空間,從而減輕伺服器的壓力,減少頻繁的查 
             詢資料庫
	○ token 完全由應用管理,所以它可以避開同源策略

Refresh Token

• 另外一種 token——refresh token
• refresh token 是專用於刷新 access token 的 token。如果沒有 refresh token,也可以刷新 access token,但每次刷新都要用戶輸入登錄用戶名與密碼,會很麻煩。有了 refresh 
    token,可以減少這個麻煩,客戶端直接用 refresh token 去更新 access token,無需用戶進行額外的操作。

• Access Token 的有效期比較短,當 Acesss Token 由於過期而失效時,使用 Refresh Token 就可以獲取到新的 Token,如果 Refresh Token 也失效了,用戶就只能重新登錄了。
• Refresh Token 及過期時間是存儲在伺服器的資料庫中,只有在申請新的 Acesss Token 時才會驗證,不會對業務介面響應時間造成影響,也不需要向 Session 一樣一直保持在記憶體中以應對 
    大量的請求。

Token 和 Session 的區別

• Session 是一種記錄伺服器和客戶端會話狀態的機制,使服務端有狀態化,可以記錄會話資訊。而 Token 是令牌,訪問資源介面(API)時所需要的資源憑證。Token 使服務端無狀態化,不會存 
   儲會話資訊。
• Session 和 Token 並不矛盾,作為身份認證 Token 安全性比 Session 好,因為每一個請求都有簽名還能防止監聽以及重放攻擊,而 Session 就必須依賴鏈路層來保障通訊安全了。如果你需 
    要實現有狀態的會話,仍然可以增加 Session 來在伺服器端保存一些狀態。
• 所謂 Session 認證只是簡單的把 User 資訊存儲到 Session 里,因為 SessionID 的不可預測性,暫且認為是安全的。而 Token ,如果指的是 OAuth Token 或類似的機制的話,提供的是 
   認證 和 授權 ,認證是針對用戶,授權是針對 App 。其目的是讓某 App 有權利訪問某用戶的資訊。這裡的 Token 是唯一的。不可以轉移到其它 App上,也不可以轉到其它用戶上。Session 只提 
    供一種簡單的認證,即只要有此 SessionID ,即認為有此 User 的全部權利。是需要嚴格保密的,這個數據應該只保存在站方,不應該共享給其它網站或者第三方 App。所以簡單來說:如果你的 
     用戶數據可能需要和第三方共享,或者允許第三方調用 API 介面,用 Token 。如果永遠只是自己的網站,自己的 App,用什麼就無所謂了。
	○ 因為存儲在客戶端,容易被客戶端篡改,使用前需要驗證合法性
	○ 不要存儲敏感數據,比如用戶密碼,賬戶餘額
	○ 使用 httpOnly 在一定程度上提高安全性
	○ 盡量減少 cookie 的體積,能存儲的數據量不能超過 4kb
	○ 設置正確的 domain 和 path,減少數據傳輸
	○ cookie 無法跨域
	○ 一個瀏覽器針對一個網站最多存 20 個Cookie,瀏覽器一般只允許存放 300 個Cookie
	○ 移動端對 cookie 的支援不是很好,而 session 需要基於 cookie 實現,所以移動端常用的是 token

使用 session 時需要考慮的問題

	○ 將 session 存儲在伺服器裡面,當用戶同時在線量比較多時,這些 session 會佔據較多的記憶體,需要在服務端定期的去清理過期的 session
	○ 當網站採用集群部署的時候,會遇到多台 web 伺服器之間如何做 session 共享的問題。因為 session 是由單個伺服器創建的,但是處理用戶請求的伺服器不一定是那個創建 
                 session 的伺服器,那麼該伺服器就無法拿到之前已經放入到 session 中的登錄憑證之類的資訊了。
	○ 當多個應用要共享 session 時,除了以上問題,還會遇到跨域問題,因為不同的應用可能部署的主機不一樣,需要在各個應用做好 cookie 跨域的處理。
	○ sessionid 是存儲在 cookie 中的,假如瀏覽器禁止 cookie 或不支援 cookie 怎麼辦? 一般會把 sessionid 跟在 url 參數後面即重寫 url,所以 session 不一定非得需要靠             
       cookie 實現
	○ 移動端對 cookie 的支援不是很好,而 session 需要基於 cookie 實現,所以移動端常用的是 token

使用 token 時需要考慮的問題

	○ 如果你認為用資料庫來存儲 token 會導致查詢時間太長,可以選擇放在記憶體當中。比如 redis 很適合你對 token 查詢的需求。
	○ token 完全由應用管理,所以它可以避開同源策略
	○ token 可以避免 CSRF 攻擊(因為不需要 cookie 了)
	○ 移動端對 cookie 的支援不是很好,而 session 需要基於 cookie 實現,所以移動端常用的是 token

使用 JWT 時需要考慮的問題

	○ 因為 JWT 並不依賴 Cookie 的,所以你可以使用任何域名提供你的 API 服務而不需要擔心跨域資源共享問題(CORS)
	○ JWT 默認是不加密,但也是可以加密的。生成原始 Token 以後,可以用密鑰再加密一次。
	○ JWT 不加密的情況下,不能將秘密數據寫入 JWT。
	○ JWT 不僅可以用於認證,也可以用於交換資訊。有效使用 JWT,可以降低伺服器查詢資料庫的次數。
	○ JWT 最大的優勢是伺服器不再需要存儲 Session,使得伺服器認證鑒權業務可以方便擴展。但這也是 JWT 最大的缺點:由於伺服器不需要存儲 Session 狀態,因此使用過程中無法廢 
               棄某個 Token 或者更改 Token 的許可權。也就是說一旦 JWT 簽發了,到期之前就會始終有效,除非伺服器部署額外的邏輯。
	○ JWT 本身包含了認證資訊,一旦泄露,任何人都可以獲得該令牌的所有許可權。為了減少盜用,JWT的有效期應該設置得比較短。對於一些比較重要的許可權,使用時應該再次對用戶進行認 
                 證。
	○ JWT 適合一次性的命令認證,頒發一個有效期極短的 JWT,即使暴露了危險也很小,由於每次操作都會生成新的 JWT,因此也沒必要保存 JWT,真正實現無狀態。
	○ 為了減少盜用,JWT 不應該使用 HTTP 協議明碼傳輸,要使用 HTTPS 協議傳輸。

使用加密演算法時需要考慮的問題

	○ 絕不要以明文存儲密碼
	○ 永遠使用 哈希演算法 來處理密碼,絕不要使用 Base64 或其他編碼方式來存儲密碼,這和以明文存儲密碼是一樣的,使用哈希,而不要使用編碼。編碼以及加密,都是雙向的過程,而密 
                 碼是保密的,應該只被它的所有者知道, 這個過程必須是單向的。哈希正是用於做這個的,從來沒有解哈希這種說法, 但是編碼就存在解碼,加密就存在解密。
	○ 絕不要使用弱哈希或已被破解的哈希演算法,像 MD5 或 SHA1 ,只使用強密碼哈希演算法。
	○ 絕不要以明文形式顯示或發送密碼,即使是對密碼的所有者也應該這樣。如果你需要 「忘記密碼」 的功能,可以隨機生成一個新的 一次性的(這點很重要)密碼,然後把這個密碼發送給 
              用戶。

分散式架構下 session 共享方案

1. session 複製
		§ 任何一個伺服器上的 session 發生改變(增刪改),該節點會把這個 session 的所有內容序列化,然後廣播給所有其它節點,不管其他伺服器需不需要 session ,以此來 
                      保證 session 同步
                  優點: 可容錯,各個伺服器間 session 能夠實時響應。 
                  缺點: 會對網路負荷造成一定壓力,如果 session 量大的話可能會造成網路堵塞,拖慢伺服器性能。
2. 粘性 session /IP 綁定策略
	      ○ 採用 Ngnix 中的 ip_hash 機制,將某個 ip的所有請求都定向到同一台伺服器上,即將用戶與伺服器綁定。 用戶第一次請求時,負載均衡器將用戶的請求轉發到了 A 伺服器                                    
                     上,如果 
                      負載均衡器設置了粘性 session 的話,那麼用戶以後的每次請求都會轉發到 A 伺服器上,相當於把用戶和 A 伺服器粘到了一塊,這就是粘性 session 機制。
                  優點: 簡單,不需要對 session 做任何處理。 
                  缺點: 缺乏容錯性,如果當前訪問的伺服器發生故障,用戶被轉移到第二個伺服器上時,他的 session 資訊都將失效。 
                  適用場景: 發生故障對客戶產生的影響較小;伺服器發生故障是低概率事件 。
                  實現方式: 以 Nginx 為例,在 upstream 模組配置 ip_hash 屬性即可實現粘性 session。
3. session 共享(常用)
	○ 使用分散式快取方案比如 Memcached 、Redis 來快取 session,但是要求 Memcached 或 Redis 必須是集群
	○ 把 session 放到 Redis 中存儲,雖然架構上變得複雜,並且需要多訪問一次 Redis ,但是這種方案帶來的好處也是很大的: 
	○ 實現了 session 共享;
	○ 可以水平擴展(增加 Redis 伺服器);
	○ 伺服器重啟 session 不丟失(不過也要注意 session 在 Redis 中的刷新/失效機制);
	○ 不僅可以跨伺服器 session 共享,甚至可以跨平台(例如網頁端和 APP 端)
4. session 持久化
	○ 將 session 存儲到資料庫中,保證 session 的持久化
            優點: 伺服器出現問題,session 不會丟失 
            缺點: 如果網站的訪問量很大,把 session 存儲到資料庫中,會對資料庫造成很大壓力,還需要增加額外的開銷維護資料庫