對比授權機制,你更想用哪種?
本文轉載自微信公眾號「Java極客技術」,作者鴨血粉絲。轉載本文請聯系Java極客技術公眾號。
授權機制,當我們說到這個問題的時候,大家對它的第一印象是在哪個地方呢?是不是曾經某培訓機構教授的 SSO 單點登錄的,是的沒錯,而這種 SSO 的單點登錄在當年的培訓機構中,使用的就是 Session 共享,也就是用 Redis 做中間模擬 Session ,但是授權機制真的有這么簡單么?接下來阿粉就來強勢對比一下關于授權機制了。
Cookie-Session 認證授權
Cookie-Session 認證機制就是為一次請求認證在服務端創建一個Session對象,同時在客戶端的瀏覽器端創建了一個Cookie對象;通過客戶端帶上來Cookie對象來與服務器端的session對象匹配來實現狀態管理的。
但是這時候我們就得考慮一下 Cookie 的存活時間了,當我們關閉瀏覽器的時候,Cookie就會被刪除,就算我們調整了 Cookie 的存活時間,但是他依然有很大的弊端,Cookie 是很容易被攔截到的,阿粉之前就看到過某個知名的 OA 系統,就曾經把用戶的ID 放到了 Cookie 中,只不過是把 Cookie 里面的鍵給設置成了 imageUrl,但是實際上這種,看著有點搞人的意思,圖片地址是一堆長的字符串,你在前端攔截到之后, 明眼人一眼就能知道這種肯定不是圖片路徑,而且當我們使用 Cookie 進行用戶識別,用戶就會很容易受到跨站請求偽造的攻擊,也就是我們經常說的 CSRF .
JWT
既然在這里對比 JWT ,我們就得先知道 JWT 是個什么東西,JSON Web Token (JWT) 實際上用大白話說,它就是一種認證機制,讓后臺知道請求是來自于受信的客戶端。
技術都是隨著問題出現的,只要有問題,那么很快就會有解決這個問題的技術出現,同樣,JWT 出現的只不過比較早而已,因為現在微服務,分布式橫行遍布,不管是大公司,還是小公司,很多都開始做分布式的項目,這做分布式也不僅僅是停留在了只存在大公司了,既然選擇使用了分布式,那么各種各樣的問題就來了。
- 跨域身份驗證
- 分布式session共享
- 分布式站點的單點登錄
JWT 是個什么玩意
我們先看一下官方網站給的內容,What is JSON Web Token?
- JSON Web Token (JWT) is an open standard (RFC 7519) that defines a compact and self-contained way for securely transmitting information between parties as a JSON object. This information can be verified and trusted because it is digitally signed. JWTs can be signed using a secret (with the HMAC algorithm) or a public/private key pair using RSA or ECDSA
JSON Web令牌(JWT)是一種開放標準(RFC7519),它定義了一種緊湊且獨立的方式,用于在各方之間安全地作為JSON對象傳輸信息。由于該信息是數字簽名的,因此可以驗證和信任此信息。可以使用秘密(使用HMAC算法)或使用RSA或ECDSA的公鑰/私鑰對對對對JWTs進行簽名.
阿粉就直接用百度翻譯了,結果翻譯出來竟然差不多,看來百度翻譯有時候也沒有那么差勁。
那么什么時候需要去使用 JWT 呢?
在官網中,給出了兩種情況下去使用 JWT ,Authorization 和 Information Exchange,一種是授權,授權我們都懂,就是當用戶登錄后,每個后續請求都將包括JWT,允許用戶訪問該令牌允許的路由、服務和資源,而資源交換,實際上簡單的說,就是在數據傳輸中用 JWT 令牌在安全地在各方之間傳輸信息
那么我們既然知道了什么時候來使用 JWT, 我們就來看看 JWT 到底是長成什么樣子,
JWT 構成:
- Header 頭部
- Payload 有效載荷
- Signature 簽名
我們從官網上獲取了一段的內容,然后逐個來看,
- eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
既然它說 JWT 是由三段信息構成的,而這三段信息,是用 .來進行隔開的,也就是上面這長串的字符串,
Header 頭部,我們看到圖里面也給出了,Header 中存儲的實際上就是2部分的內容。typ:類型 alg:加密算法,
然后他是對頭部進行的 Base64 加密,我就是我們在官網摘下來的第一段的內容,就出現了加密字符串 eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9 。
Payload 有效載荷
實際上有效載荷實際上就是存儲有效信息的地方,那么他都存儲了一些什么內容呢?
- iss (issuer):簽發人
- exp (expiration time):過期時間
- sub (subject):主題
- aud (audience):受眾
- nbf (Not Before):生效時間
- iat (Issued At):簽發時間
jti (JWT ID):編號( jwt的唯一身份標識,主要用來作為一次性token,從而回避重放攻擊)
為什么會有這么多,因為在 JWT 的規范中,他告訴我們的是,建議但不強制使用,也就是說,你可以根據自身的應用去選擇使用,比如官網給出的,他就沒有寫全面,就使用了三個:
- {
- "sub": "1234567890",
- "name": "John Doe",
- "iat": 1516239022
- }
而實際上這部分的內容卻是比較重要的內容。
signature簽名信息
實際上這個就是一個組裝起來的,將頭部和載荷用’.'號連接,再加上一串密鑰,經過頭部聲明的加密算法加密后得到簽名。
這個 Header 和 Payload 都是加密過的,而在這個地方它還進行了 “加鹽” 的操作,將這三部分用.連接成一個完整的字符串,構成了最終的jwt
jwt其實并不是什么高深莫測的技術,在很多技術人的眼中,可能覺得他會非常的low ,實際上雖然不高端,但是也沒有那么 low,認證服務器通過對稱或非對稱的加密方式利用payload生成signature,并在header中申明簽名方式,這就是 JWT 的本質實現方式。
JWT 的有點其實很明顯,
- 通過驗證簽名的方式可以直接在資源服務器本地完成授權校驗
- 在payload中可以包含用戶相關信息,實現了token和用戶信息的綁定
使用場景一般是用在一次性的身份驗證上,千萬不要想著去用 JWT 去代替 Session,雖然 JWT 可以設置失效時間,但是在有效期內,它是無法作廢的。
OAuth2認證
OAuth 引入了一個授權層,用來分離兩種不同的角色:客戶端和資源所有者。資源所有者同意以后,資源服務器可以向客戶端頒發令牌。客戶端通過令牌,去請求數據。
其實這個 OAuth 的核心就是向第三方應用頒發令牌,而在 Oauth2 中定義了四種獲得令牌的流程,也就是通俗的四種授權方式,但是我們經常使用的也就是那么一種。
- 授權碼
- 隱藏式(簡化)
- 密碼式
- 客戶端憑證
- 授權碼模式
這是在 Oauth 里面的功能算是最完整的,而且流程最嚴密的授權模式。
授權碼模式的步驟:
- 1.用戶訪問客戶端,后者將前者導向認證服務器
- 2.用戶選擇是否給予客戶端授權
- 3.假設用戶給予授權,認證服務器將用戶導向客戶端事先指定的"重定向URI"(redirection URI),同時附上一個授權碼
- 4.客戶端收到授權碼,附上早先的"重定向URI",向認證服務器申請令牌。這一步是在客戶端的后臺的服務器上完成的,對用戶不可見
- 5.認證服務器核對了授權碼和重定向URI,確認無誤后,向客戶端發送訪問令牌(access token)和更新令牌(refresh token)
其實授權碼模式就相當于是第三方的應用去先申請一個授權碼,然后再用該授權碼獲取令牌。
總結下來就是四個步驟 :
1:請求授權碼
2:返回授權碼
3:請求令牌
4:返回令牌
我們給出一個例子,然后分析一下。
- https://2.com/oauth/authorize? //授權地址
- response_type=code& //參數1:response_type :這里表示授權的類型,此處的值固定為"code"
- client_id=CLIENT_ID& //參數2:client_id :表示客戶端的ID
- redirect_uri=CALLBACK_URL& //參數3:redirect_uri :表示重定向URL
- scope=read //參數4:scope: 表示申請的權限范圍
上面的地址,就相當于第一步,攜帶所需要的參數請求 網站2,請求獲取授權碼。
- https://1.com/callback?code=AUTHORIZATION_CODE //code 授權碼
上面的地址就是第二步了,網站2給網站1返回授權碼,
- https://2.com/oauth/token?
- client_id=CLIENT_ID& 客戶端ID
- client_secret=CLIENT_SECRET& 客戶端密鑰
- grant_type=authorization_code& 使用的授權模式 authorization_code :授權碼模式
- code=AUTHORIZATION_CODE& 授權碼
- redirect_uri=CALLBACK_URL 表示重定向URL
上面的地址就到第三步了,用授權碼去索要令牌的請求就發送了。
請求發送完成后,2網站收到請求之后,這時候就向 重定向URL 發送以下的 JSON 數據,
- {
- "access_token":"ACCESS_TOKEN", //訪問令牌
- "token_type":"bearer",// 令牌類型
- "expires_in":2592000, // 過期時間
- "refresh_token":"REFRESH_TOKEN", // 更新令牌
- "scope":"read", // 權限范圍 只讀
- "uid":100101, //
- "info":{...} //
- }
這時候 訪問令牌 我們就要有了,這完成所有的步驟后,我們就拿到了我們訪問的令牌了,也就是我們完成了所需要的授權了。
隱藏式
其實隱藏式就是簡化版的授權模式,他省略了獲取 授權碼 的過程,而是直接請求獲取 令牌 的過程。
案例如下:
- https://2.com/oauth/authorize?
- response_type=token& 授權的類型,此處的值固定為"token"
- client_id=CLIENT_ID& 客戶端ID
- redirect_uri=CALLBACK_URL& 表示重定向URL
- scope=read 權限范圍 只讀
上面授權類型直接就是索要令牌,
第二步也很簡單,就是直接給你返回你需要的令牌
- https://1.com/callback#token=ACCESS_TOKEN
上面的 Token 就是我們需要的令牌了,
密碼式
這種為什么稱之為 密碼式 ,是因為它在請求的時候,是用密碼去換令牌,這就需要一個前提,你對這個網站有高度的信用度,如果你不信用他,他給你賬號密碼作用都不大,給了你也不會授權給它 Token 不是么。
案例步驟如下:
1.請求令牌
- https://oauth.2.com/token?
- grant_type=password& 授權方式:指定為密碼式
- username=USERNAME& 用戶名
- password=PASSWORD& 密碼
- client_id=CLIENT_ID 客戶端ID
2.返回令牌
- https://1.com/callback#token=ACCESS_TOKEN
這個感覺和隱藏式差距不大,一個是直接要,一個是拿著參數要。
憑證式
這個憑證式的步驟也是比較少的,實際上阿粉感覺這種方式不知道算不算是授權的方式,因為這種模式是客戶端以自己的名義向"授權服務提供者"進行認證,但是既然說是,那就暫且的認定他是,
1:請求令牌
- https://oauth.2.com/token?
- grant_type=client_credentials& 授權方式:憑證式
- client_id=CLIENT_ID& 客戶端ID
- client_secret=CLIENT_SECRET 客戶端密鑰
2.返回令牌
- https://1.com/callback#token=ACCESS_TOKEN
這種方式給出的令牌,是針對第三方應用的,而不是針對用戶的,也就是說可能出現多個用戶共享同一個令牌。
為什么要比較 JWT 和Oauth2 ,因為很多不明所以的人總是會在挑選技術的時候,會把二者拿出來對比,其實上,他們兩個沒有可比性,因為 JWT 是用于發布接入令牌,并對發布的簽名接入令牌進行驗證的方法。
OAuth2是一種授權框架,授權第三方應用訪問特定資源。
也就是說:
- OAuth2用在使用第三方賬號登錄的情況
- JWT是用在前后端分離, 需要簡單的對后臺API進行保護
所以你知道怎么選擇了么?
文章參考
《阮一峰的網絡日志》 《JWT官方文檔》