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