https://juejin.im/post/5e055d9ef265da33997a42cc
什么是認證(Authentication)
- 通俗地講就是驗證當前用戶的身份,證明“你是你自己”(比如:你每天上下班打卡,都需要通過指紋打卡,當你的指紋和系統里錄入的指紋相匹配時,就打卡成功)
- 互聯網中的認證:
- 用戶名密碼登錄
- 郵箱發送登錄鏈接
- 手機號接收驗證碼
- 只要你能收到郵箱/驗證碼,就默認你是賬號的主人
什么是授權(Authorization)
- 用戶授予第三方應用訪問該用戶某些資源的權限
- 你在安裝手機應用的時候,APP 會詢問是否允許授予權限(訪問相冊、地理位置等權限)
- 你在訪問微信小程式時,當登錄時,小程式會詢問是否允許授予權限(獲取昵稱、頭像、地區、性別等個人資訊)
- 實作授權的方式有:cookie、session、token、OAuth
什么是憑證(Credentials)
- 實作認證和授權的前提是需要一種媒介(證書) 來標記訪問者的身份
- 在戰國時期,商鞅變法,發明了照身帖,照身帖由官府發放,是一塊打磨光滑細密的竹板,上面刻有持有人的頭像和籍貫資訊,國人必須持有,如若沒有就被認為是黑戶,或者間諜之類的,
- 在現實生活中,每個人都會有一張專屬的居民身份證,是用于證明持有人身份的一種法定證件,通過身份證,我們可以辦理手機卡/銀行卡/個人貸款/交通出行等等,這就是認證的憑證,
- 在互聯網應用中,一般網站(如掘金)會有兩種模式,游客模式和登錄模式,游客模式下,可以正常瀏覽網站上面的文章,一旦想要點贊/收藏/分享文章,就需要登錄或者注冊賬號,當用戶登錄成功后,服務器會給該用戶使用的瀏覽器頒發一個令牌(token),這個令牌用來表明你的身份,每次瀏覽器發送請求時會帶上這個令牌,就可以使用游客模式下無法使用的功能,
什么是 Cookie
- HTTP 是無狀態的協議(對于事務處理沒有記憶能力,每次客戶端和服務端會話完成時,服務端不會保存任何會話資訊):每個請求都是完全獨立的,服務端無法確認當前訪問者的身份資訊,無法分辨上一次的請求發送者和這一次的發送者是不是同一個人,所以服務器與瀏覽器為了進行會話跟蹤(知道是誰在訪問我),就必須主動的去維護一個狀態,這個狀態用于告知服務端前后兩個請求是否來自同一瀏覽器,而這個狀態需要通過 cookie 或者 session 去實作,
- cookie 存盤在客戶端: cookie 是服務器發送到用戶瀏覽器并保存在本地的一小塊資料,它會在瀏覽器下次向同一服務器再發起請求時被攜帶并發送到服務器上,
- cookie 是不可跨域的: 每個 cookie 都會系結單一的域名,無法在別的域名下獲取使用,一級域名和二級域名之間是允許共享使用的(靠的是 domain),
cookie 重要的屬性
| 屬性 | 說明 |
|---|---|
| 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 的一道橋梁,大部分系統也是根據此原理來驗證用戶登錄狀態,
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 的身份驗證流程:
- 客戶端使用用戶名跟密碼請求登錄
- 服務端收到請求,去驗證用戶名與密碼
- 驗證成功后,服務端會簽發一個 token 并把這個 token 發送給客戶端
- 客戶端收到 token 以后,會把它存盤起來,比如放在 cookie 里或者 localStorage 里
- 客戶端每次向服務端請求資源的時候需要帶著服務端簽發的 token
- 服務端收到請求,然后去驗證客戶端請求里面帶著的 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,用什么就無所謂了,
什么是 JWT
- JSON Web Token(簡稱 JWT)是目前最流行的跨域認證解決方案,
- 是一種認證授權機制,
- JWT 是為了在網路應用環境間傳遞宣告而執行的一種基于 JSON 的開放標準(RFC 7519),JWT 的宣告一般被用來在身份提供者和服務提供者間傳遞被認證的用戶身份資訊,以便于從資源服務器獲取資源,比如用在用戶登錄上,
- 可以使用 HMAC 演算法或者是 RSA 的公/私秘鑰對 JWT 進行簽名,因為數字簽名的存在,這些傳遞的資訊是可信的,
- 阮一峰老師的 JSON Web Token 入門教程 講的非常通俗易懂,這里就不再班門弄斧了
生成 JWT
jwt.io/
www.jsonwebtoken.io/
JWT 的原理
- JWT 認證流程:
- 用戶輸入用戶名/密碼登錄,服務端認證成功后,會回傳給客戶端一個 JWT
- 客戶端將 token 保存到本地(通常使用 localstorage,也可以使用 cookie)
- 當用戶希望訪問一個受保護的路由或者資源的時候,需要請求頭的 Authorization 欄位中使用Bearer 模式添加 JWT,其內容看起來是下面這樣
Authorization: Bearer <token>
復制代碼
- 服務端的保護路由將會檢查請求頭 Authorization 中的 JWT 資訊,如果合法,則允許用戶的行為
- 因為 JWT 是自包含的(內部包含了一些會話資訊),因此減少了需要查詢資料庫的需要
- 因為 JWT 并不使用 Cookie 的,所以你可以使用任何域名提供你的 API 服務而不需要擔心跨域資源共享問題(CORS)
- 因為用戶的狀態不再存盤在服務端的記憶體中,所以這是一種無狀態的認證機制
JWT 的使用方式
- 客戶端收到服務器回傳的 JWT,可以儲存在 Cookie 里面,也可以儲存在 localStorage,
方式一
-
當用戶希望訪問一個受保護的路由或者資源的時候,可以把它放在 Cookie 里面自動發送,但是這樣不能跨域,所以更好的做法是放在 HTTP 請求頭資訊的 Authorization 欄位里,使用 Bearer 模式添加 JWT,
GET /calendar/v1/events Host: api.example.com Authorization: Bearer <token> 復制代碼- 用戶的狀態不會存盤在服務端的記憶體中,這是一種 無狀態的認證機制
- 服務端的保護路由將會檢查請求頭 Authorization 中的 JWT 資訊,如果合法,則允許用戶的行為,
- 由于 JWT 是自包含的,因此減少了需要查詢資料庫的需要
- JWT 的這些特性使得我們可以完全依賴其無狀態的特性提供資料 API 服務,甚至是創建一個下載流服務,
- 因為 JWT 并不使用 Cookie ,所以你可以使用任何域名提供你的 API 服務而不需要擔心跨域資源共享問題(CORS)
方式二
- 跨域的時候,可以把 JWT 放在 POST 請求的資料體里,
方式三
- 通過 URL 傳輸
http://www.example.com/user?token=xxx
復制代碼
專案中使用 JWT
專案地址
Token 和 JWT 的區別
相同:
- 都是訪問資源的令牌
- 都可以記錄用戶的資訊
- 都是使服務端無狀態化
- 都是只有驗證成功后,客戶端才能訪問服務端上受保護的資源
區別:
- Token:服務端驗證客戶端發送過來的 Token 時,還需要查詢資料庫獲取用戶資訊,然后驗證 Token 是否有效,
- JWT: 將 Token 和 Payload 加密后存盤于客戶端,服務端只需要使用密鑰解密進行校驗(校驗也是 JWT 自己實作的)即可,不需要查詢或者減少查詢資料庫,因為 JWT 自包含了用戶資訊和加密的資料,
常見的前后端鑒權方式
- Session-Cookie
- Token 驗證(包括 JWT,SSO)
- OAuth2.0(開放授權)
常見的加密演算法
- 哈希演算法(Hash Algorithm)又稱散列演算法、散列函式、哈希函式,是一種從任何一種資料中創建小的數字“指紋”的方法,哈希演算法將資料重新打亂混合,重新創建一個哈希值,
- 哈希演算法主要用來保障資料真實性(即完整性),即發信人將原始訊息和哈希值一起發送,收信人通過相同的哈希函式來校驗原始資料是否真實,
- 哈希演算法通常有以下幾個特點:
- 正像快速:原始資料可以快速計算出哈希值
- 逆向困難:通過哈希值基本不可能推匯出原始資料
- 輸入敏感:原始資料只要有一點變動,得到的哈希值差別很大
- 沖突避免:很難找到不同的原始資料得到相同的哈希值,宇宙中原子數大約在 10 的 60 次方到 80 次方之間,所以 2 的 256 次方有足夠的空間容納所有的可能,演算法好的情況下沖突碰撞的概率很低:
- 2 的 128 次方為 340282366920938463463374607431768211456,也就是 10 的 39 次方級別
- 2 的 160 次方為 1.4615016373309029182036848327163e+48,也就是 10 的 48 次方級別
- 2 的 256 次方為 1.1579208923731619542357098500869 × 10 的 77 次方,也就是 10 的 77 次方
注意:
- 以上不能保證資料被惡意篡改,原始資料和哈希值都可能被惡意篡改,要保證不被篡改,可以使用RSA 公鑰私鑰方案,再配合哈希值,
- 哈希演算法主要用來防止計算機傳輸程序中的錯誤,早期計算機通過前 7 位資料第 8 位奇偶校驗碼來保障(12.5% 的浪費效率低),對于一段資料或檔案,通過哈希演算法生成 128bit 或者 256bit 的哈希值,如果校驗有問題就要求重傳,
常見問題
使用 cookie 時需要考慮的問題
- 因為存盤在客戶端,容易被客戶端篡改,使用前需要驗證合法性
- 不要存盤敏感資料,比如用戶密碼,賬戶余額
- 使用 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 存盤到資料庫中,會對資料庫造成很大壓力,還需要增加額外的開銷維護資料庫,
只要關閉瀏覽器 ,session 真的就消失了?
不對,對 session 來說,除非程式通知服務器洗掉一個 session,否則服務器會一直保留,程式一般都是在用戶做 log off 的時候發個指令去洗掉 session,
然而瀏覽器從來不會主動在關閉之前通知服務器它將要關閉,因此服務器根本不會有機會知道瀏覽器已經關閉,之所以會有這種錯覺,是大部分 session 機制都使用會話 cookie 來保存 session id,而關閉瀏覽器后這個 session id 就消失了,再次連接服務器時也就無法找到原來的 session,如果服務器設定的 cookie 被保存在硬碟上,或者使用某種手段改寫瀏覽器發出的 HTTP 請求頭,把原來的 session id 發送給服務器,則再次打開瀏覽器仍然能夠打開原來的 session,
恰恰是由于關閉瀏覽器不會導致 session 被洗掉,迫使服務器為 session 設定了一個失效時間,當距離客戶端上一次使用 session 的時間超過這個失效時間時,服務器就認為客戶端已經停止了活動,才會把 session 洗掉以節省存盤空間,
專案地址
作者:秋天不落葉
鏈接:https://juejin.im/post/5e055d9ef265da33997a42cc
來源:掘金
著作權歸作者所有,商業轉載請聯系作者獲得授權,非商業轉載請注明出處,
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/6606.html
標籤:其他
上一篇:Windows粘滯鍵后門
