在上篇[認證受權] 1.OAuth2受權 中介紹到了OAuth2能夠幫咱們解決第三方Client訪問受保護資源的問題,可是隻提供瞭如何得到access_token,並未說明怎麼來撤銷一個access_token。關於這部分OAuth2單獨定義了一個RFC7009 - OAuth 2.0 Token Revocation來解決撤銷Token問題。html
OAuth2提供的「access_token"是一個對Client不透明的字符串,儘管有"scope","expires_in"和"refresh_token"來輔助,但也是不完善的且分散的信息。還拿上一篇的小明來舉例,「小明受權在線打印而且包郵的網站訪問本身的QQ空間的相冊」。雙引號裏面的這句話其中有4個重要的概念:web
那麼如何獲得得到上面提到的這些附加的信息呢?OAuth2又單獨提供了一個RFC7662 -OAuth 2.0 Token Introspection來解決Token的描述信息不完整的問題。算法
這些信息不但對Client不透明,對於資源服務器來講也是不透明的,好比受權服務器和資源服務器是獨立部署的,而OAuth2又要求資源服務器要對access token作校驗,沒有這些信息如何校驗呢?除非在access token的db存儲層面作共享,可是做爲一個運行在互聯網規模上的網絡環境下的協議,這種假設是沒法支撐互聯網規模的環境的。數據庫
簡單來講,這個協議規定了一個Authorization server提供一個怎樣的API來供Client撤銷access_token或者refresh_token。json
好比Client發起一個以下的請求:安全
POST /revoke HTTP/1.1
Host: server.example.com
Content-Type: application/x-www-form-urlencoded
Authorization: Basic czZCaGRSa3F0MzpnWDFmQmF0M2JW
token=45ghiukldjahdnhzdauz&token_type_hint=refresh_token服務器
其中各項含義以下:cookie
若是撤銷成功,則返回一個HTTP status code爲200的響應就能夠了。網絡
簡單的總結來講,這個規範是爲OAuth2擴展了一個API接口(Introspection Endpoint),讓第三方Client能夠查詢上面提到的那些信息(好比,access_token是否還有效,誰頒發的,頒發給誰的,scope又哪些等等的元數據信息)。app
好比Client發起一個以下的請求:
POST /introspect HTTP/1.1
Host: server.example.com
Accept: application/json
Content-Type: application/x-www-form-urlencoded
Authorization: Bearer 23410913-abewfq.123483
token=2YotnFZFEjr1zCsicMWpAA&token_type_hint=access_token
看起來和上面的撤銷Token的請求差很少,其中各項含義以下:
若是請求成功,則會返回以下的信息:
1 { 2 "active": true, 3 "client_id": "l238j323ds-23ij4", 4 "token_type":"access_token", 5 "username": "jdoe", 6 "scope": "read write dolphin", 7 "sub": "Z5O3upPC88QrAjx00dis", 8 "aud": "https://protected.example.net/resource", 9 "iss": "https://server.example.com/", 10 "exp": 1419356238, 11 "iat": 1419350238, 12 "nbf": 1419350238, 13 "jti": "abcdefg" 14 "extension_field": "twenty-seven" 15 }
JSON各項屬性含義以下(其中有些信息是在JSON Web Token中定義的,參考連接有詳細的介紹):
其中大量的信息都是可選的信息,並且能夠本身擴展須要的屬性信息,從這些屬性中就能夠解決咱們上面提到的access_token對於Client不透明的問題。
咱們注意到其中有不少屬於JWT定義的屬性,那麼這個JWT是什麼東西?它解決了什麼問題?
簡單總結來講,JWT是一個定義一種緊湊的,自包含的而且提供防篡改機制的傳遞數據的方式的標準協議。
咱們先來看一個簡單的示例:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6Imxpbmlhbmh1aSJ9.hnOfZb95jFwQsYj3qlgFbUu1rKpfTE6AzgXZidEGGTk
就是這麼一堆看起來像是亂碼同樣的字符串。JWT由3部分構成:header.payload.signature,每一個部分由「.」來分割開來。
header是一個有效的JSON,其中一般包含了兩部分:token類型和簽名算法。
{ "alg": "HS256", "typ": "JWT" }
對這個JSON採用base64編碼後就是第1部分eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9。
這一部分表明真正想要傳遞的數據,包含一組Claims,其中JWT預約義了一些Claim(2. Token 元數據 這一節就用到一些JWT預約義的一些Cliam)後面會介紹。關於什麼是Claim,能夠參考文章末尾給的參考連接。
{ "sub": "1234567890", "name": "linianhui" }
對這個JSON採用base64編碼後就是第2部分eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6Imxpbmlhbmh1aSJ9。
這一部分是可選的,因爲前面Header和Payload部分是明文的信息,因此這一部分的意義在於保障信息不被篡改用的,生成這部分的方式以下:
HMACSHA256( base64UrlEncode(header) + "." + base64UrlEncode(payload), secret)
token生成方使用header中指定的簽名算法對「header.payload」部分進行簽名,獲得的第3部分hnOfZb95jFwQsYj3qlgFbUu1rKpfTE6AzgXZidEGGTk,而後組合成一個完整的JWT字符串 . 而token消費方在拿到token後, 使用一樣的簽名算法來生成簽名,用來判斷header和payload部分有沒有被篡改過,由於簽名的密鑰是隻有通訊雙方知道的,因此能夠保證這部分信息不被第三方所篡改。
JWT規範中預先定義了一些Cliam,但並非必選的,經常使用的有:
iss(Issuer簽發者)
。sub(subject簽發給的受衆,在Issuer範圍內是惟一的)
。aud(Audience接收方)
。exp(Expiration Time過時時間)
。iat(Issued At簽發時間)等等。
更完整的一些Claim列表參見:https://www.iana.org/assignments/jwt/jwt.xhtml
若是上面這些仍沒法知足本身的須要,則能夠自定義一些來使用。
因爲其採用base64來進行編碼,使得它能夠安全的用在一些僅限ASCII的地方傳遞信息,好比URL的querystring中。
好比用戶登錄後,能夠把用戶的一些屬性信息(用戶標識,是不是管理員,權限有哪些等等能夠公開的信息)用JWT編碼存儲在cookie中,因爲其自包含的性質,每次服務器讀取到Cookie的時候就能夠解析到當前用戶對應的屬性信息,而沒必要再次去查詢數據庫。若是Cookie中每次都發送浪費帶寬,也能夠用 Authorization: Bearer <jwttoken> 的方式附加到Request上去。
注意到咱們在2. Token 元數據 這一小節中,OAuth2返回Token的元數據的JSON,以及OAuth2中的access_token對Client是不透明的字符串這件事,咱們能夠把access_token的元數據信息用JWT來編碼如下,做爲access_token的字符串內容,這樣是否是就可使得它對Client是透明的了。
好比我以前遇到的問題,在我使用access_token的時候有沒有過時我並不知道,其實須要藉助輔助的「expires_in」來檢查,還有其scope是哪些,也須要額外的去查詢,再好比這個access_token管理的用戶是誰,也須要額外的查詢,有了JWT呢,能夠把這些都打包進去,好比:
{ "sub":"linianhui", "scope":"1419356238", "exp":123456789, }
而後生成一個這樣的jwt字符串 eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJsaW5pYW5odWkiLCJzY29wZSI6IjE0MTkzNTYyMzgiLCJleHAiOjEyMzQ1Njc4OX0.ASu85ohHMSOhnxbJSJI4OKLsPlbjPs7th0Xw5-b4l1A 做爲access_token的值,感受一會兒就方便了好多吧。
OAuth2在RFC6749中並未完整的提供一些問題的解決方案,而是附加了一些相關的RFC來解決這些問題,其實除了本文中提到的2個問題點以外,還有一些其餘能夠優化的地方存在(好比服務發現:https://tools.ietf.org/html/draft-ietf-oauth-discovery-06),From Post Response Mode :http://openid.net/specs/oauth-v2-form-post-response-mode-1_0.html),這些點在後續的OIDC的文章中再作介紹吧,感興趣的能夠看一看http://openid.net/connect/中關於OAuth2的另一些相關擴展標準草案,這些標準也是OIDC所須要的一些可選支持;以及OAuth相關擴展草案:https://datatracker.ietf.org/wg/oauth/charter/。另外在一些場景下,使用JWT來使得OAuth2的提供自包含的Token仍是一件很方便的事情的。
以上內容均是我的的一些理解,若是錯誤之處,歡迎指正!
JSON協議:RFC7159 - The JavaScript Object Notation (JSON) Data Interchange Format
OAuth2 擴展協議:
RFC7009 - OAuth 2.0 Token Revocation
RFC7662 - OAuth 2.0 Token Introspection
OAuth相關擴展草案:
https://datatracker.ietf.org/wg/oauth/charter/
https://tools.ietf.org/wg/oauth/
JWT相關協議族:
RFC7515 - JSON Web Signature (JWS)
RFC7516 - JSON Web Encryption (JWE)
RFC7518 - JSON Web Algorithms (JWA)
RFC7519 - JSON Web Token (JWT)
JWT官方站點:https://jwt.io
Claims:https://en.wikipedia.org/wiki/Claims-based_identity
JWT註冊的的一組Claims : https://www.iana.org/assignments/jwt/jwt.xhtml