自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

前后端分離開(kāi)發(fā),HTTP API 認(rèn)證授權(quán)術(shù)

安全
這個(gè)世界還是比較復(fù)雜的,除了用戶訪問(wèn),還有用戶委托的第三方的應(yīng)用,還有企業(yè)和企業(yè)間的調(diào)用,這里,我想把業(yè)內(nèi)常用的一些 API認(rèn)證技術(shù)相對(duì)系統(tǒng)地總結(jié)歸納一下,這樣可以讓大家更為全面的了解這些技術(shù)。

 我們知道,HTTP 是無(wú)狀態(tài)的,所以,當(dāng)我們需要獲得用戶是否在登錄的狀態(tài)時(shí),我們需要檢查用戶的登錄狀態(tài),一般來(lái)說(shuō),用戶的登錄成功后,服務(wù)器會(huì)發(fā)一個(gè)登錄憑證(又被叫作 Token),就像你去訪問(wèn)某個(gè)公司,在前臺(tái)被認(rèn)證過(guò)合法后,這個(gè)公司的前臺(tái)會(huì)給你的一個(gè)訪客卡一樣,之后,你在這個(gè)公司內(nèi)去到哪都用這個(gè)訪客卡來(lái)開(kāi)門(mén),而不再校驗(yàn)?zāi)闶悄囊粋€(gè)人。在計(jì)算機(jī)的世界里,這個(gè)登錄憑證的相關(guān)數(shù)據(jù)會(huì)放在兩種地方,一個(gè)地方在用戶端,以 Cookie 的方式(一般不會(huì)放在瀏覽器的 Local Storage,因?yàn)檫@很容易出現(xiàn)登錄憑證被 XSS 攻擊),另一個(gè)地方是放在服務(wù)器端,又叫 Session 的方式(SessonID 存于 Cookie)。

[[285595]]

但是,這個(gè)世界還是比較復(fù)雜的,除了用戶訪問(wèn),還有用戶委托的第三方的應(yīng)用,還有企業(yè)和企業(yè)間的調(diào)用,這里,我想把業(yè)內(nèi)常用的一些 API認(rèn)證技術(shù)相對(duì)系統(tǒng)地總結(jié)歸納一下,這樣可以讓大家更為全面的了解這些技術(shù)。注意,這是一篇長(zhǎng)文!

本篇文章會(huì)覆蓋如下技術(shù):

  • HTTP Basic
  • Digest Access
  • App Secret Key + HMAC
  • JWT – JSON Web Tokens
  • OAuth 1.0 – 3 legged & 2 legged
  • OAuth 2.0 – Authentication Code & Client Credential

HTTP Basic

HTTP Basic 是一個(gè)非常傳統(tǒng)的 API 認(rèn)證技術(shù),也是一個(gè)比較簡(jiǎn)單的技術(shù)。這個(gè)技術(shù)也就是使用 username 和 password 來(lái)進(jìn)行登錄。整個(gè)過(guò)程被定義在了 RFC 2617 中,也被描述在了 Wikipedia: Basic Access Authentication 詞條中,同時(shí)也可以參看 MDN HTTP Authentication

其技術(shù)原理如下:

  1. 把 username和 password 做成 username:password 的樣子(用冒號(hào)分隔)
  2. 進(jìn)行 Base64 編碼。Base64("username:password") 得到一個(gè)字符串(如:把 haoel:coolshell 進(jìn)行 base64 后可以得到 aGFvZW86Y29vbHNoZWxsCg )
  3. 把 aGFvZW86Y29vbHNoZWxsCg 放到 HTTP 頭中 Authorization 字段中,形成 Authorization: Basic aGFvZW86Y29vbHNoZWxsCg,然后發(fā)送到服務(wù)端。
  4. 服務(wù)端如果沒(méi)有在頭里看到認(rèn)證字段,則返回 401 錯(cuò),以及一個(gè)個(gè) WWW-Authenticate: Basic Realm='HelloWorld' 之類(lèi)的頭要求客戶端進(jìn)行認(rèn)證。之后如果沒(méi)有認(rèn)證通過(guò),則返回一個(gè) 401 錯(cuò)。如果服務(wù)端認(rèn)證通過(guò),那么會(huì)返回 200。

我們可以看到,使用 Base64 的目的無(wú)非就是為了把一些特殊的字符給搞掉,這樣就可以放在 HTTP 協(xié)議里傳輸了。而這種方式的問(wèn)題最大的問(wèn)題就是把用戶名和口令放在網(wǎng)絡(luò)上傳,所以,一般要配合 TLS/SSL 的安全加密方式來(lái)使用。我們可以看到 JIRA Cloud 的 API 認(rèn)證支持HTTP Basic 這樣的方式。

但我們還是要知道,這種把用戶名和密碼同時(shí)放在公網(wǎng)上傳輸?shù)姆绞接悬c(diǎn)不太好,因?yàn)?Base64 不是加密協(xié)議,而是編碼協(xié)議,所以就算是有 HTTPS 作為安全保護(hù),給人的感覺(jué)還是不放心。

Digest Access

中文稱“HTTP 摘要認(rèn)證”,最初被定義在了 RFC 2069 文檔中(后來(lái)被 RFC 2617 引入了一系列安全增強(qiáng)的選項(xiàng);“保護(hù)質(zhì)量”(qop)、隨機(jī)數(shù)計(jì)數(shù)器由客戶端增加、以及客戶生成的隨機(jī)數(shù))。

其基本思路是,請(qǐng)求方把用戶名口令和域做一個(gè) MD5 – MD5(username:realm:password) 然后傳給服務(wù)器,這樣就不會(huì)在網(wǎng)上傳用戶名和口令了,但是,因?yàn)橛脩裘涂诹罨静粫?huì)變,所以,這個(gè) MD5 的字符串也是比較固定的,因此,這個(gè)認(rèn)證過(guò)程在其中加入了兩個(gè)事,一個(gè)是 nonce 另一個(gè)是 qop

  • 首先,調(diào)用方發(fā)起一個(gè)普通的 HTTP 請(qǐng)求。比如:GET /coolshell/admin/ HTTP/1.1 服務(wù)端自然不能認(rèn)證能過(guò),服務(wù)端返回 401 錯(cuò)誤,并且在 HTTP 頭里的 WWW-Authenticate 包含如下信息:
  1. WWW-Authenticate: Digest realm="testrealm@host.com"
  2.                        qop="auth,auth-int"
  3.                        nonce="dcd98b7102dd2f0e8b11d0f600bfb0c093"
  4.                        opaque="5ccc069c403ebaf9f0171e9517f40e41" 
  • 其中的 nonce 為服務(wù)器端生成的隨機(jī)數(shù),然后,客戶端做 HASH1=MD5(MD5(username:realm:password):nonce:cnonce) ,其中的 cnonce 為客戶端生成的隨機(jī)數(shù),這樣就可以使得整個(gè) MD5 的結(jié)果是不一樣的。
  • 如果 qop 中包含了 auth ,那么還得做 HASH2=MD5(method:digestURI) 其中的 method 就是HTTP的請(qǐng)求方法(GET/POST…),digestURI 是請(qǐng)求的URL。
  • 如果 qop 中包含了 auth-init ,那么,得做 HASH2=MD5(method:digestURI:MD5(entityBody)) 其中的 entityBody 就是HTTP請(qǐng)求的整個(gè)數(shù)據(jù)體。
  • 然后,得到 response = MD5(HASH1:nonce:nonceCount:cnonce:qop:HASH2) 如果沒(méi)有 qop 則 response = MD5(HA1:nonce:HA2)
  • 最后,我們的客戶端對(duì)服務(wù)端發(fā)起如下請(qǐng)求—— 注意HTTP頭的 Authorization: Digest ...
  1. GET /dir/index.html HTTP/1.0 
  2. Host: localhost 
  3. Authorization: Digest username="Mufasa"
  4.                      realm="testrealm@host.com"
  5.                      nonce="dcd98b7102dd2f0e8b11d0f600bfb0c093"
  6.                      uri="%2Fcoolshell%2Fadmin"
  7.                      qop=auth, 
  8.                      nc=00000001, 
  9.                      cnonce="0a4f113b"
  10.                      response="6629fae49393a05397450978507c4ef1"
  11.                      opaque="5ccc069c403ebaf9f0171e9517f40e41" 

維基百科上的 Wikipedia: Digest access authentication 詞條非常詳細(xì)地描述了這個(gè)細(xì)節(jié)。

摘要認(rèn)證這個(gè)方式會(huì)比之前的方式要好一些,因?yàn)闆](méi)有在網(wǎng)上傳遞用戶的密碼,而只是把密碼的 MD5 傳送過(guò)去,相對(duì)會(huì)比較安全,而且,其并不需要是否 TLS/SSL 的安全鏈接。但是,別看這個(gè)算法這么復(fù)雜,最后你可以發(fā)現(xiàn),整個(gè)過(guò)程其實(shí)關(guān)鍵是用戶的 password,這個(gè) password 如果不夠得雜,其實(shí)是可以被暴力破解的,而且,整個(gè)過(guò)程是非常容易受到中間人攻擊——比如一個(gè)中間人告訴客戶端需要的 Basic 的認(rèn)證方式 或是 老舊簽名認(rèn)證方式(RFC2069)。

App Secret Key + HMAC

先說(shuō) HMAC 技術(shù),這個(gè)東西來(lái)自于 MAC – Message Authentication Code,是一種用于給消息簽名的技術(shù),也就是說(shuō),我們怕消息在傳遞的過(guò)程中被人修改,所以,我們需要用對(duì)消息進(jìn)行一個(gè) MAC 算法,得到一個(gè)摘要字串,然后,接收方得到消息后,進(jìn)行同樣的計(jì)算,然后比較這個(gè) MAC 字符串,如果一致,則表明沒(méi)有被修改過(guò)(整個(gè)過(guò)程參看下圖)。而 HMAC – Hash-based Authenticsation Code,指的是利用 Hash 技術(shù)完成這一工作,比如:SHA-256算法。

 

我們?cè)賮?lái)說(shuō) App ID,這個(gè)東西跟驗(yàn)證沒(méi)有關(guān)系,只是用來(lái)區(qū)分,是誰(shuí)來(lái)調(diào)用 API 的,就像我們每個(gè)人的身份證一樣,只是用來(lái)標(biāo)注不同的人,不是用來(lái)做身份認(rèn)證的。與前面的不同之處是,這里,我們需要用 App ID 來(lái)映射一個(gè)用于加密的密鑰,這樣一來(lái),我們就可以在服務(wù)器端進(jìn)行相關(guān)的管理,我們可以生成若干個(gè)密鑰對(duì)(AppID, AppSecret),并可以有更細(xì)粒度的操作權(quán)限管理。

  1. 把 AppID 和 HMAC 用于 API 認(rèn)證,目前來(lái)說(shuō),玩得最好最專(zhuān)業(yè)的應(yīng)該是 AWS 了,我們可以通過(guò) S3 的 API 請(qǐng)求簽名文檔看到 AWS 是怎么玩的。整個(gè)過(guò)程還是非常復(fù)雜的,可以通過(guò)下面的圖片流程看個(gè)大概。基本上來(lái)說(shuō),分成如下幾個(gè)步驟:
  2. 把 HTTP 的請(qǐng)求(方法、URI、查詢字串、頭、簽名頭,body)打個(gè)包叫 CanonicalRequest,作個(gè) SHA-256 的簽名,然后再做一個(gè) base16 的編碼
  3. 把上面的這個(gè)簽名和簽名算法 AWS4-HMAC-SHA256、時(shí)間戳、Scop,再打一個(gè)包,叫 StringToSign。
  4. 準(zhǔn)備簽名,用 AWSSecretAccessKey 來(lái)對(duì)日期簽一個(gè) DataKey,再用 DataKey 對(duì)要操作的 Region 簽一個(gè) DataRegionKey ,再對(duì)相關(guān)的服務(wù)簽一個(gè) DataRegionServiceKey ,最后得到 SigningKey.
  5. 用第三步的 SigningKey 來(lái)對(duì)第二步的 StringToSign 簽名。

 

最后,發(fā)出 HTTP Request 時(shí),在 HTTP 頭的 Authorization 字段中放入如下的信息:

  1. Authorization: AWS4-HMAC-SHA256 
  2.                Credential=AKIDEXAMPLE/20150830/us-east-1/iam/aws4_request, 
  3.                SignedHeaders=content-type;host;x-amz-date
  4.                Signature=5d672d79c15b13162d9279b0855cfba6789a8edb4c82c400e06b5924a6f2b5d7 

其中的 AKIDEXAMPLE 是 AWS Access Key ID, 也就是所謂的 AppID,服務(wù)器端會(huì)根據(jù)這個(gè) AppID 來(lái)查相關(guān)的 Secret Access Key,然后再驗(yàn)證簽名。如果,你對(duì)這個(gè)過(guò)程有點(diǎn)沒(méi)看懂的話,你可以讀一讀這篇文章——《Amazon S3 Rest API with curl》這篇文章里有好些代碼,代碼應(yīng)該是最有細(xì)節(jié)也是最準(zhǔn)確的了。

這種認(rèn)證的方式好處在于,AppID 和 AppSecretKey,是由服務(wù)器的系統(tǒng)開(kāi)出的,所以,是可以被管理的,AWS 的 IAM 就是相關(guān)的管理,其管理了用戶、權(quán)限和其對(duì)應(yīng)的 AppID 和AppSecretKey。但是不好的地方在于,這個(gè)東西沒(méi)有標(biāo)準(zhǔn) ,所以,各家的實(shí)現(xiàn)很不一致。比如:Acquia 的 HMAC,微信的簽名算法 (這里,我們需要說(shuō)明一下,微信的 API 沒(méi)有遵循HTTP 協(xié)議的標(biāo)準(zhǔn),把認(rèn)證信息放在 HTTP 頭的 Authorization 里,而是放在 body 里)

JWT – JSON Web Tokens

JWT 是一個(gè)比較標(biāo)準(zhǔn)的認(rèn)證解決方案,這個(gè)技術(shù)在 Java 圈里應(yīng)該用的是非常普遍的。JWT 簽名也是一種 MAC(Message Authentication Code)的方法。JWT 的簽名流程一般是下面這個(gè)樣子:

用戶使用用戶名和口令到認(rèn)證服務(wù)器上請(qǐng)求認(rèn)證。

認(rèn)證服務(wù)器驗(yàn)證用戶名和口令后,以服務(wù)器端生成 JWT Token,這個(gè) token 的生成過(guò)程如下:

  • 認(rèn)證服務(wù)器還會(huì)生成一個(gè) Secret Key(密鑰)
  • 對(duì)JWT Header和 JWT Payload 分別求 Base64。在 Payload 可能包括了用戶的抽象 ID 和的過(guò)期時(shí)間。
  • 用密鑰對(duì) JWT 簽名 HMAC-SHA256(SecertKey, Base64UrlEncode(JWT-Header)+'.'+Base64UrlEncode(JWT-Payload));

然后把 base64(header).base64(payload).signature 作為 JWT token 返回客戶端。

客戶端使用 JWT Token 向應(yīng)用服務(wù)器發(fā)送相關(guān)的請(qǐng)求。這個(gè) JWT Token 就像一個(gè)臨時(shí)用戶權(quán)證一樣。

當(dāng)應(yīng)用服務(wù)器收到請(qǐng)求后:

  1. 應(yīng)用服務(wù)會(huì)檢查 JWT Token,確認(rèn)簽名是正確的。
  2. 然而,因?yàn)橹挥姓J(rèn)證服務(wù)器有這個(gè)用戶的 Secret Key(密鑰),所以,應(yīng)用服務(wù)器得把 JWT Token 傳給認(rèn)證服務(wù)器。
  3. 認(rèn)證服務(wù)器通過(guò) JWT Payload 解出用戶的抽象 ID,然后通過(guò)抽象 ID 查到登錄時(shí)生成的 Secret Key,然后再來(lái)檢查一下簽名。
  4. 認(rèn)證服務(wù)器檢查通過(guò)后,應(yīng)用服務(wù)就可以認(rèn)為這是合法請(qǐng)求了。

我們可以看以,上面的這個(gè)過(guò)程,是在認(rèn)證服務(wù)器上為用戶動(dòng)態(tài)生成 Secret Key 的,應(yīng)用服務(wù)在驗(yàn)簽的時(shí)候,需要到認(rèn)證服務(wù)器上去簽,這個(gè)過(guò)程增加了一些網(wǎng)絡(luò)調(diào)用,所以,JWT 除了支持 HMAC-SHA256 的算法外,還支持 RSA 的非對(duì)稱加密的算法。

使用 RSA 非對(duì)稱算法,在認(rèn)證服務(wù)器這邊放一個(gè)私鑰,在應(yīng)用服務(wù)器那邊放一個(gè)公鑰,認(rèn)證服務(wù)器使用私鑰加密,應(yīng)用服務(wù)器使用公鑰解密,這樣一來(lái),就不需要應(yīng)用服務(wù)器向認(rèn)證服務(wù)器請(qǐng)求了,但是,RSA 是一個(gè)很慢的算法,所以,雖然你省了網(wǎng)絡(luò)調(diào)用,但是卻費(fèi)了 CPU,尤其是Header 和 Payload 比較長(zhǎng)的時(shí)候。所以,一種比較好的玩法是,如果我們把 header 和 payload 簡(jiǎn)單地做 SHA256,這會(huì)很快,然后,我們用 RSA 加密這個(gè) SHA256 出來(lái)的字符串,這樣一來(lái),RSA 算法就比較快了,而我們也做到了使用 RSA 簽名的目的。

最后,我們只需要使用一個(gè)機(jī)制在認(rèn)證服務(wù)器和應(yīng)用服務(wù)器之間定期地?fù)Q一下公鑰私鑰對(duì)就好了。

這里強(qiáng)烈建議全文閱讀 Anglar 大學(xué)的 《JSW:The Complete Guide to JSON Web Tokens》

OAuth 1.0

OAuth 也是一個(gè) API 認(rèn)證的協(xié)議,這個(gè)協(xié)議最初在 2006 年由 Twitter 的工程師在開(kāi)發(fā) OpenID 實(shí)現(xiàn)的時(shí)候和社交書(shū)簽網(wǎng)站 Ma.gnolia 時(shí)發(fā)現(xiàn),沒(méi)有一種好的委托授權(quán)協(xié)議,后來(lái)在 2007 年成立了一個(gè) OAuth 小組,知道這個(gè)消息后,Google 員工也加入進(jìn)來(lái),并完善有善了這個(gè)協(xié)議,在 2007 年底發(fā)布草案,過(guò)一年后,在 2008 年將 OAuth 放進(jìn)了 IETF 作進(jìn)一步的標(biāo)準(zhǔn)化工作,最后在 2010 年 4 月,正式發(fā)布 OAuth 1.0,即:RFC 5849 (這個(gè) RFC 比起 TCP 的那些來(lái)說(shuō)讀起來(lái)還是很輕松的),不過(guò),如果你想了解其前身的草案,可以讀一下 OAuth Core 1.0 Revision A ,我在下面做個(gè)大概的描述。

根據(jù)RFC 5849,可以看到 OAuth 的出現(xiàn),目的是為了,用戶為了想使用一個(gè)第三方的網(wǎng)絡(luò)打印服務(wù)來(lái)打印他在某網(wǎng)站上的照片,但是,用戶不想把自己的用戶名和口令交給那個(gè)第三方的網(wǎng)絡(luò)打印服務(wù),但又想讓那個(gè)第三方的網(wǎng)絡(luò)打印服務(wù)來(lái)訪問(wèn)自己的照片,為了解決這個(gè)授權(quán)的問(wèn)題, OAuth 這個(gè)協(xié)議就出來(lái)了。

這個(gè)協(xié)議有三個(gè)角色:

  • User(照片所有者-用戶)
  • Consumer(第三方照片打印服務(wù))
  • Service Provider(照片存儲(chǔ)服務(wù))

這個(gè)協(xié)義有三個(gè)階段:

  • Consumer 獲取 Request Token
  • Service Provider 認(rèn)證用戶并授權(quán) Consumer
  • Consumer 獲取 Access Token 調(diào)用 API 訪問(wèn)用戶的照片

整個(gè)授權(quán)過(guò)程是這樣的:

  1. Consumer(第三方照片打印服務(wù))需要先上 Service Provider 獲得開(kāi)發(fā)的 Consumer Key 和 Consumer Secret
  2. 當(dāng) User 訪問(wèn) Consumer 時(shí),Consumer 向 Service Provide 發(fā)起請(qǐng)求請(qǐng)求Request Token (需要對(duì)HTTP請(qǐng)求簽名)
  3. Service Provide 驗(yàn)明 Consumer 是注冊(cè)過(guò)的第三方服務(wù)商后,返回 Request Token(oauth_token)和 Request Token Secret (oauth_token_secret)
  4. Consumer 收到 Request Token 后,使用 HTTP GET 請(qǐng)求把 User 切到 Service Provide 的認(rèn)證頁(yè)上(其中帶上Request Token),讓用戶輸入他的用戶和口令。
  5. Service Provider 認(rèn)證 User 成功后,跳回 Consumer,并返回 Request Token (oauth_token)和 Verification Code(oauth_verifier)
  6. 接下來(lái)就是簽名請(qǐng)求,用 Request Token 和 Verification Code 換取 Access Token (oauth_token)和 Access Token Secret (oauth_token_secret)
  7. 最后使用 Access Token 訪問(wèn)用戶授權(quán)訪問(wèn)的資源。

下圖附上一個(gè) Yahoo! 的流程圖可以看到整個(gè)過(guò)程的相關(guān)細(xì)節(jié)。

 

因?yàn)樯厦孢@個(gè)流程有三方:User,Consumer 和 Service Provide,所以,又叫 3-legged flow,三腳流程。OAuth 1.0 也有不需要用戶參與的,只有 Consumer 和 Service Provider 的, 也就是 2-legged flow 兩腳流程,其中省掉了用戶認(rèn)證的事。整個(gè)過(guò)程如下所示:

  1. Consumer(第三方照片打印服務(wù))需要先上 Service Provider 獲得開(kāi)發(fā)的 Consumer Key 和 Consumer Secret
  2. Consumer 向 Service Provide 發(fā)起請(qǐng)求請(qǐng)求 Request Token (需要對(duì) HTTP 請(qǐng)求簽名)
  3. Service Provide 驗(yàn)明 Consumer 是注冊(cè)過(guò)的第三方服務(wù)商后,返回 Request Token(oauth_token)和 Request Token Secret (oauth_token_secret)
  4. Consumer 收到 Request Token 后,直接換取 Access Token (oauth_token)和 Access Token Secret (oauth_token_secret)
  5. 最后使用 Access Token 訪問(wèn)用戶授權(quán)訪問(wèn)的資源。

最后,再來(lái)說(shuō)一說(shuō) OAuth 中的簽名。

  • 我們可以看到,有兩個(gè)密鑰,一個(gè)是 Consumer 注冊(cè) Service Provider 時(shí)由 Provider 頒發(fā)的 Consumer Secret,另一個(gè)是 Token Secret。
  • 簽名密鑰就是由這兩具密鑰拼接而成的,其中用 & 作連接符。假設(shè) Consumer Secret 為 j49sk3j29djd 而 Token Secret 為 dh893hdasih9 那個(gè),簽名密鑰為:j49sk3j29djd&dh893hdasih9
  • 在請(qǐng)求 Request/Access Token 的時(shí)候需要對(duì)整個(gè) HTTP 請(qǐng)求進(jìn)行簽名(使用 HMAC-SHA1 和 HMAC-RSA1 簽名算法),請(qǐng)求頭中需要包括一些 OAuth 需要的字段,如:
    • Consumer Key :也就是所謂的 AppID
    • Token:Request Token 或 Access Token
    • Signature Method :簽名算法比如:HMAC-SHA1
    • Timestamp:過(guò)期時(shí)間
    • Nonce:隨機(jī)字符串
    • Call Back:回調(diào) URL

下圖是整個(gè)簽名的示意圖:

 

圖片還是比較直觀的,我就不多解釋了。

OAuth 2.0

在前面,我們可以看到,從 Digest Access, 到 AppID+HMAC,再到 JWT,再到 OAuth 1.0,這些個(gè) API 認(rèn)證都是要向 Client發(fā)一個(gè)密鑰(或是用密碼)然后用 HASH 或是 RSA 來(lái)簽 HTTP 的請(qǐng)求,這其中有個(gè)主要的原因是,以前的 HTTP 是明文傳輸,所以,在傳輸過(guò)程中很容易被篡改,于是才搞出來(lái)一套的安全簽名機(jī)制,所以,這些個(gè)認(rèn)證的玩法是可以在 HTTP 明文協(xié)議下玩的。

這種使用簽名方式大家可以看到是比較復(fù)雜的,所以,對(duì)于開(kāi)發(fā)者來(lái)說(shuō),也是很不友好的,在組織簽名的那些 HTTP 報(bào)文的時(shí)候,各種,URLEncode 和 Base64,還要對(duì) Query 的參數(shù)進(jìn)行排序,然后有的方法還要層層簽名,非常容易出錯(cuò),另外,這種認(rèn)證的安全粒度比較粗,授權(quán)也比較單一,對(duì)于有終端用戶參與的移動(dòng)端來(lái)說(shuō)也有點(diǎn)不夠。所以,在 2012 年的時(shí)候,OAuth 2.0 的 RFC 6749 正式放出。

OAuth 2.0 依賴于 TLS/SSL 的鏈路加密技術(shù)(HTTPS),完全放棄了簽名的方式,認(rèn)證服務(wù)器再也不返回什么 token secret 的密鑰了,所以,OAuth 2.0 是完全不同于 1.0 的,也是不兼容的。目前,F(xiàn)acebook 的 Graph API 只支持 OAuth 2.0協(xié)議,Google 和 Microsoft Azure 也支持Auth 2.0,國(guó)內(nèi)的微信和支付寶也支持使用 OAuth 2.0。

下面,我們來(lái)重點(diǎn)看一下 OAuth 2.0 的兩個(gè)主要的 Flow:

  • 一個(gè)是 Authorization Code Flow, 這個(gè)是 3 legged 的
  • 一個(gè)是 Client Credential Flow,這個(gè)是 2 legged 的。

Authorization Code Flow

Authorization Code 是最常使用的 OAuth 2.0 的授權(quán)許可類(lèi)型,它適用于用戶給第三方應(yīng)用授權(quán)訪問(wèn)自己信息的場(chǎng)景。這個(gè) Flow 也是 OAuth 2.0 四個(gè) Flow 中我個(gè)人覺(jué)得最完整的一個(gè) Flow,其流程圖如下所示。

 

下面是對(duì)這個(gè)流程的一個(gè)細(xì)節(jié)上的解釋?zhuān)?/p>

1)當(dāng)用戶(Resource Owner)訪問(wèn)第三方應(yīng)用(Client)的時(shí)候,第三方應(yīng)用會(huì)把用戶帶到認(rèn)證服務(wù)器(Authorization Server)上去,主要請(qǐng)求的是 /authorize API,其中的請(qǐng)求方式如下所示。

  1. https://login.authorization-server.com/authorize? 
  2.         client_id=6731de76-14a6-49ae-97bc-6eba6914391e 
  3.         &response_type=code 
  4.         &redirect_uri=http%3A%2F%2Fexample-client.com%2Fcallback%2F 
  5.         &scope=read 
  6.         &state=xcoiv98CoolShell3kch 

其中:

  • client_id 為第三方應(yīng)用的 App ID
  • response_type=code 為告訴認(rèn)證服務(wù)器,我要走 Authorization Code Flow。
  • redirect_uri 意思是我跳轉(zhuǎn)回第三方應(yīng)用的 URL
  • scope 意是相關(guān)的權(quán)限
  • state 是一個(gè)隨機(jī)的字符串,主要用于防 CSRF 攻擊。

2)當(dāng) Authorization Server 收到這個(gè) URL 請(qǐng)求后,其會(huì)通過(guò) client_id 來(lái)檢查 redirect_uri 和 scope 是否合法,如果合法,則彈出一個(gè)頁(yè)面,讓用戶授權(quán)(如果用戶沒(méi)有登錄,則先讓用戶登錄,登錄完成后,出現(xiàn)授權(quán)訪問(wèn)頁(yè)面)。

3)當(dāng)用戶授權(quán)同意訪問(wèn)以后,Authorization Server 會(huì)跳轉(zhuǎn)回 Client ,并以其中加入一個(gè) Authorization Code。如下所示:

  1. https://example-client.com/callback? 
  2.         code=Yzk5ZDczMzRlNDEwYlrEqdFSBzjqfTG 
  3.         &state=xcoiv98CoolShell3kch 

我們可以看到,

  • 請(qǐng)流動(dòng)的鏈接是第 1)步中的 redirect_uri
  • 其中的 state 的值也和第 1)步的 state一樣。

4)接下來(lái),Client 就可以使用 Authorization Code 獲得 Access Token。其需要向 Authorization Server 發(fā)出如下請(qǐng)求。

  1. POST /oauth/token HTTP/1.1 
  2. Host: authorization-server.com 
  3.   
  4. code=Yzk5ZDczMzRlNDEwYlrEqdFSBzjqfTG 
  5. &grant_type=code 
  6. &redirect_uri=https%3A%2F%2Fexample-client.com%2Fcallback%2F 
  7. &client_id=6731de76-14a6-49ae-97bc-6eba6914391e 
  8. &client_secret=JqQX2PNo9bpM0uEihUPzyrh 

5)如果沒(méi)什么問(wèn)題,Authorization 會(huì)返回如下信息。

  1.   "access_token""iJKV1QiLCJhbGciOiJSUzI1NiI"
  2.   "refresh_token""1KaPlrEqdFSBzjqfTGAMxZGU"
  3.   "token_type""bearer"
  4.   "expires": 3600, 
  5.   "id_token""eyJ0eXAiOiJKV1QiLCJhbGciO.eyJhdWQiOiIyZDRkM..." 

其中,

  • access_token 就是訪問(wèn)請(qǐng)求令牌了
  • refresh_token 用于刷新 access_token
  • id_token 是 JWT 的 token,其中一般會(huì)包含用戶的 OpenID

6)接下來(lái)就是用 Access Token 請(qǐng)求用戶的資源了。

  1. GET /v1/user/pictures 
  2. Host: https://example.resource.com 
  3.  
  4. Authorization: Bearer iJKV1QiLCJhbGciOiJSUzI1NiI 

Client Credential Flow

Client Credential 是一個(gè)簡(jiǎn)化版的 API 認(rèn)證,主要是用于認(rèn)證服務(wù)器到服務(wù)器的調(diào)用,也就是沒(méi)有用戶參與的的認(rèn)證流程。下面是相關(guān)的流程圖。

 

這個(gè)過(guò)程非常簡(jiǎn)單,本質(zhì)上就是 Client 用自己的 client_id 和 client_secret 向Authorization Server 要一個(gè) Access Token,然后使用 Access Token 訪問(wèn)相關(guān)的資源。

請(qǐng)求示例

  1. POST /token HTTP/1.1 
  2. Host: server.example.com 
  3. Content-Type: application/x-www-form-urlencoded 
  4.  
  5. grant_type=client_credentials 
  6. &client_id=czZCaGRSa3F0Mzpn 
  7. &client_secret=7Fjfp0ZBr1KtDRbnfVdmIw 

返回示例

  1.   "access_token":"MTQ0NjJkZmQ5OTM2NDE1ZTZjNGZmZjI3"
  2.   "token_type":"bearer"
  3.   "expires_in":3600, 
  4.   "refresh_token":"IwOGYzYTlmM2YxOTQ5MGE3YmNmMDFkNTVk"
  5.   "scope":"create" 

這里,容我多扯一句,微信公從平臺(tái)的開(kāi)發(fā)文檔中,使用了 OAuth 2.0 的 Client Credentials 的方式(參看文檔“微信公眾號(hào)獲取 access token”),我截了個(gè)圖如下所謂。我們可以看到,微信公眾號(hào)使用的是 GET 方式的請(qǐng)求,把 AppID 和 AppSecret 放在了 URL中,雖然這也符合 OAuth 2.0,但是并不好,因?yàn)榇蠖鄶?shù)網(wǎng)關(guān)代理會(huì)把整個(gè) URI 請(qǐng)求記到日志中。我們只要腦補(bǔ)一下騰訊的網(wǎng)關(guān)的 Access Log,里面的日志一定會(huì)有很多的各個(gè)用戶的AppID 和 AppSecret……

 


 

 

小結(jié)

講了這么多,我們來(lái)小結(jié)一下(下面的小結(jié)可能會(huì)有點(diǎn)散)

兩個(gè)概念和三個(gè)術(shù)語(yǔ)

  • 區(qū)分兩個(gè)概念:Authentication(認(rèn)證) 和 Authorization (授權(quán)),前者是證明請(qǐng)求者是身份,就像身份證一樣,后者是為了獲得權(quán)限。身份是區(qū)別于別人的證明,而權(quán)限是證明自己的特權(quán)。Authentication 為了證明操作的這個(gè)人就是他本人,需要提供密碼、短信驗(yàn)證碼,甚至人臉識(shí)別。Authorization 則是不需要在所有的請(qǐng)求都需要驗(yàn)人,是在經(jīng)過(guò) Authorization 后得到一個(gè) Token,這就是 Authorization。就像護(hù)照和簽證一樣。
  • 區(qū)分三個(gè)概念:編碼 Base64Encode、簽名 HMAC、加密 RSA。編碼是為了更的傳輸,等同于明文,簽名是為了信息不能被篡改,加密是為了不讓別人看到是什么信息。

明白一些初衷

  • 使用復(fù)雜地 HMAC 哈希簽名方式主要是應(yīng)對(duì)當(dāng)年沒(méi)有 TLS/SSL 加密鏈路的情況。
  • JWT 把 uid 放在 Token 中目的是為了去掉狀態(tài),但不能讓用戶修改,所以需要簽名。
  • OAuth 1.0 區(qū)分了兩個(gè)事,一個(gè)是第三方的 Client,一個(gè)是真正的用戶,其先拿 Request Token,再換 Access Token 的方法主要是為了把第三方應(yīng)用和用戶區(qū)分開(kāi)來(lái)。
  • 用戶的 Password 是用戶自己設(shè)置的,復(fù)雜度不可控,服務(wù)端頒發(fā)的 Serect 會(huì)很復(fù)雜,但主要目的是為了容易管理,可以隨時(shí)注銷(xiāo)掉。
  • OAuth 協(xié)議有比所有認(rèn)證協(xié)議有更為靈活完善的配置,如果使用 AppID/AppSecret 簽名的方式,又需要做到可以有不同的權(quán)限和可以隨時(shí)注銷(xiāo),那么你得開(kāi)發(fā)一個(gè)像 AWS 的 IAM 這樣的賬號(hào)和密鑰對(duì)管理的系統(tǒng)。

相關(guān)的注意事項(xiàng)

  • 無(wú)論是哪種方式,我們都應(yīng)該遵循 HTTP 的規(guī)范,把認(rèn)證信息放在 Authorization HTTP 頭中。
  • 不要使用 GET 的方式在 URL 中放入 secret 之類(lèi)的東西,因?yàn)楹芏?proxy 或 gateway 的軟件會(huì)把整個(gè) URL 記在 Access Log 文件中。
  • 密鑰 Secret 相當(dāng)于 Password,但他是用來(lái)加密的,最好不要在網(wǎng)絡(luò)上傳輸,如果要傳輸,最好使用 TLS/SSL 的安全鏈路。
  • HMAC 中無(wú)論是 MD5 還是 SHA1/SHA2,其計(jì)算都是非??斓模琑SA 的非對(duì)稱加密是比較耗 CPU 的,尤其是要加密的字符串很長(zhǎng)的時(shí)候。
  • 最好不要在程序中 hard code 你的 Secret,因?yàn)樵?github 上有很多黑客的軟件在監(jiān)視各種 Secret,千萬(wàn)小心!這類(lèi)的東西應(yīng)該放在你的配置系統(tǒng)或是部署系統(tǒng)中,在程序啟動(dòng)時(shí)設(shè)置在配置文件或是環(huán)境變量中。
  • 使用 AppID/AppSecret,還是使用 OAuth1.0a,還是 OAuth2.0,還是使用 JWT,我個(gè)人建議使用 TLS/SSL 下的 OAuth 2.0。
  • 密鑰是需要被管理的,管理就是可以新增可以撤銷(xiāo),可以設(shè)置賬戶和相關(guān)的權(quán)限。最好密鑰是可以被自動(dòng)更換的。
  • 認(rèn)證授權(quán)服務(wù)器(Authorization Server)和應(yīng)用服務(wù)器(App Server)最好分開(kāi)。

 

責(zé)任編輯:武曉燕 來(lái)源: 江南一點(diǎn)雨
相關(guān)推薦

2023-09-01 14:27:10

前段后端開(kāi)發(fā)

2022-09-06 10:26:38

前后端分離Vue跨域

2022-04-06 07:50:57

JWT后端Spring

2020-09-25 11:50:12

前后端分離架構(gòu)Web

2023-11-30 12:13:29

開(kāi)發(fā)分離工具

2019-06-12 19:00:14

前后端分離AppJava

2023-02-08 16:29:58

前后端開(kāi)發(fā)

2020-04-20 14:50:02

前端技巧優(yōu)化

2023-09-21 10:44:41

Web服務(wù)Swagger前端

2021-09-18 09:45:33

前端接口架構(gòu)

2017-02-15 10:18:32

架構(gòu)前后端分離

2021-10-20 18:21:18

項(xiàng)目技術(shù)開(kāi)發(fā)

2014-04-18 14:43:07

前后端分離NodeJS

2019-07-09 05:44:35

前后端分離架構(gòu)接口規(guī)范

2018-10-23 14:24:10

2015-07-01 15:32:39

前端前后端分離

2016-08-22 13:31:05

前端架構(gòu)前后端分離

2017-11-15 07:01:33

互聯(lián)網(wǎng)分層架構(gòu)前后端

2015-04-21 11:18:20

Web系統(tǒng)開(kāi)發(fā)構(gòu)架前后端

2019-12-04 08:44:59

前后端分離開(kāi)發(fā)
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)