轉(zhuǎn)自 https://coolshell.cn/articles/19395.html
我們知道,HTTP是無狀態(tài)的奕短,所以,當(dāng)我們需要獲得用戶是否在登錄的狀態(tài)時,我們需要檢查用戶的登錄狀態(tài)瀑焦,一般來說,用戶的登錄成功后梗肝,服務(wù)器會發(fā)一個登錄憑證(又被叫作Token)榛瓮,就像你去訪問某個公司,在前臺被認(rèn)證過合法后巫击,這個公司的前臺會給你的一個訪客卡一樣禀晓,之后,你在這個公司內(nèi)去到哪都用這個訪客卡來開門坝锰,而不再校驗?zāi)闶悄囊粋€人粹懒。在計算機的世界里,這個登錄憑證的相關(guān)數(shù)據(jù)會放在兩種地方顷级,一個地方在用戶端崎淳,以Cookie的方式(一般不會放在瀏覽器的Local Storage,因為這很容易出現(xiàn)登錄憑證被XSS攻擊),另一個地方是放在服務(wù)器端拣凹,又叫Session的方式(SessonID存于Cookie)森爽。
但是,這個世界還是比較復(fù)雜的嚣镜,除了用戶訪問爬迟,還有用戶委托的第三方的應(yīng)用,還有企業(yè)和企業(yè)間的調(diào)用菊匿,這里付呕,我想把業(yè)內(nèi)常用的一些 API認(rèn)證技術(shù)相對系統(tǒng)地總結(jié)歸納一下,這樣可以讓大家更為全面的了解這些技術(shù)跌捆。注意徽职,這是一篇長文!
本篇文章會覆蓋如下技術(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
目錄
<nav style="box-sizing: border-box; display: block; color: rgb(66, 66, 66); font-family: "Source Sans Pro", sans-serif; font-size: 18px; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: justify; text-indent: 0px; text-transform: none; white-space: normal; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;">
</nav>
HTTP Basic
HTTP Basic 是一個非常傳統(tǒng)的API認(rèn)證技術(shù)佩厚,也是一個比較簡單的技術(shù)姆钉。這個技術(shù)也就是使用 username
和 password
來進行登錄。整個過程被定義在了 RFC 2617 中抄瓦,也被描述在了 Wikipedia: Basic Access Authentication 詞條中潮瓶,同時也可以參看 MDN HTTP Authentication
其技術(shù)原理如下:
- 把
username
和password
做成username:password
的樣子(用冒號分隔) - 進行Base64編碼。
Base64("username:password")
得到一個字符串(如:把haoel:coolshell
進行base64 后可以得到aGFvZW86Y29vbHNoZWxsCg
) - 把
aGFvZW86Y29vbHNoZWxsCg
放到HTTP頭中Authorization
字段中钙姊,形成Authorization: Basic aGFvZW86Y29vbHNoZWxsCg
毯辅,然后發(fā)送到服務(wù)端。 - 服務(wù)端如果沒有在頭里看到認(rèn)證字段煞额,則返回401錯思恐,以及一個個
[WWW-Authenticate](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/WWW-Authenticate): Basic Realm='HelloWorld'
之類的頭要求客戶端進行認(rèn)證。之后如果沒有認(rèn)證通過膊毁,則返回一個401錯胀莹。如果服務(wù)端認(rèn)證通過,那么會返回200媚媒。
我們可以看到,使用Base64的目的無非就是為了把一些特殊的字符給搞掉涩僻,這樣就可以放在HTTP協(xié)議里傳輸了缭召。而這種方式的問題最大的問題就是把用戶名和口令放在網(wǎng)絡(luò)上傳,所以逆日,一般要配合TLS/SSL的安全加密方式來使用嵌巷。我們可以看到 JIRA Cloud 的API認(rèn)證支持HTTP Basic 這樣的方式。
但我們還是要知道室抽,這種把用戶名和密碼同時放在公網(wǎng)上傳輸?shù)姆绞接悬c不太好搪哪,因為Base64不是加密協(xié)議,而是編碼協(xié)議坪圾,所以就算是有HTTPS作為安全保護晓折,給人的感覺還是不放心惑朦。
Digest Access
中文稱“HTTP 摘要認(rèn)證”,最初被定義在了 RFC 2069 文檔中(后來被 RFC 2617 引入了一系列安全增強的選項漓概;“保護質(zhì)量”(qop)漾月、隨機數(shù)計數(shù)器由客戶端增加、以及客戶生成的隨機數(shù))胃珍。
其基本思路是梁肿,請求方把用戶名口令和域做一個MD5 – MD5(username:realm:password)
然后傳給服務(wù)器,這樣就不會在網(wǎng)上傳用戶名和口令了觅彰,但是吩蔑,因為用戶名和口令基本不會變,所以填抬,這個MD5的字符串也是比較固定的烛芬,因此,這個認(rèn)證過程在其中加入了兩個事痴奏,一個是 nonce
另一個是 qop
- 首先蛀骇,調(diào)用方發(fā)起一個普通的HTTP請求。比如:
GET /coolshell/admin/ HTTP/1.1
- 服務(wù)端自然不能認(rèn)證能過读拆,服務(wù)端返回401錯誤擅憔,并且在HTTP頭里的
WWW-Authenticate
包含如下信息:
WWW-Authenticate: Digest realm="testrealm@host.com",
qop="auth,auth-int",
nonce="dcd98b7102dd2f0e8b11d0f600bfb0c093",
opaque="5ccc069c403ebaf9f0171e9517f40e41"</pre>
- 其中的
nonce
為服務(wù)器端生成的隨機數(shù),然后檐晕,客戶端做HASH1=MD5(MD5(username:realm:password):nonce:cnonce)
暑诸,其中的cnonce
為客戶端生成的隨機數(shù),這樣就可以使得整個MD5的結(jié)果是不一樣的辟灰。 - 如果
qop
中包含了auth
个榕,那么還得做HASH2=MD5(method:digestURI)
其中的method
就是HTTP的請求方法(GET/POST…),digestURI
是請求的URL芥喇。 - 如果
qop
中包含了auth-init
西采,那么,得做HASH2=MD5(method:digestURI:MD5(entityBody))
其中的entityBody
就是HTTP請求的整個數(shù)據(jù)體继控。 - 然后械馆,得到
response = MD5(HASH1:nonce:nonceCount:cnonce:qop:HASH2)
如果沒有qop
則response = MD5(HA1:nonce:HA2)
- 最后,我們的客戶端對服務(wù)端發(fā)起如下請求—— 注意HTTP頭的
Authorization: Digest ...
GET /dir/index.html HTTP/1.0
Host: localhost
Authorization: Digest username="Mufasa",
realm="testrealm@host.com",
nonce="dcd98b7102dd2f0e8b11d0f600bfb0c093",
uri="%2Fcoolshell%2Fadmin",
qop=auth,
nc=00000001,
cnonce="0a4f113b",
response="6629fae49393a05397450978507c4ef1",
opaque="5ccc069c403ebaf9f0171e9517f40e41"</pre>
維基百科上的 Wikipedia: Digest access authentication 詞條非常詳細(xì)地描述了這個細(xì)節(jié)武通。
摘要認(rèn)證這個方式會比之前的方式要好一些霹崎,因為沒有在網(wǎng)上傳遞用戶的密碼,而只是把密碼的MD5傳送過去冶忱,相對會比較安全尾菇,而且,其并不需要是否TLS/SSL的安全鏈接。但是派诬,別看這個算法這么復(fù)雜劳淆,最后你可以發(fā)現(xiàn),整個過程其實關(guān)鍵是用戶的password千埃,這個password如果不夠得雜憔儿,其實是可以被暴力破解的,而且放可,整個過程是非常容易受到中間人攻擊——比如一個中間人告訴客戶端需要的 Basic 的認(rèn)證方式 或是 老舊簽名認(rèn)證方式(RFC2069)谒臼。
App Secret Key + HMAC
先說HMAC技術(shù),這個東西來自于MAC – Message Authentication Code耀里,是一種用于給消息簽名的技術(shù)蜈缤,也就是說,我們怕消息在傳遞的過程中被人修改冯挎,所以底哥,我們需要用對消息進行一個MAC算法,得到一個摘要字串房官,然后趾徽,接收方得到消息后,進行同樣的計算翰守,然后比較這個MAC字符串孵奶,如果一致,則表明沒有被修改過(整個過程參看下圖)蜡峰。而HMAC – Hash-based Authenticsation Code了袁,指的是利用Hash技術(shù)完成這一工作,比如:SHA-256算法湿颅。
(圖片來自 Wikipedia – MAC 詞條 )
我們再來說App ID载绿,這個東西跟驗證沒有關(guān)系,只是用來區(qū)分油航,是誰來調(diào)用API的崭庸,就像我們每個人的身份證一樣,只是用來標(biāo)注不同的人谊囚,不是用來做身份認(rèn)證的怕享。與前面的不同之處是,這里秒啦,我們需要用App ID 來映射一個用于加密的密鑰熬粗,這樣一來搀玖,我們就可以在服務(wù)器端進行相關(guān)的管理余境,我們可以生成若干個密鑰對(AppID, AppSecret),并可以有更細(xì)粒度的操作權(quán)限管理。
把AppID和HMAC用于API認(rèn)證芳来,目前來說含末,玩得最好最專業(yè)的應(yīng)該是AWS了,我們可以通過S3的API請求簽名文檔看到AWS是怎么玩的即舌。整個過程還是非常復(fù)雜的佣盒,可以通過下面的圖片流程看個大概⊥缒簦基本上來說肥惭,分成如下幾個步驟:
- 把HTTP的請求(方法、URI紊搪、查詢字串蜜葱、頭、簽名頭耀石,body)打個包叫
CanonicalRequest
牵囤,作個SHA-256的簽名,然后再做一個base16的編碼 - 把上面的這個簽名和簽名算法
AWS4-HMAC-SHA256
滞伟、時間戳揭鳞、Scop,再打一個包梆奈,叫StringToSign
野崇。 - 準(zhǔn)備簽名,用
AWSSecretAccessKey
來對日期簽一個DataKey
鉴裹,再用DataKey
對要操作的Region簽一個DataRegionKey
舞骆,再對相關(guān)的服務(wù)簽一個DataRegionServiceKey
,最后得到SigningKey
. - 用第三步的
SigningKey
來對第二步的StringToSign
簽名径荔。
最后督禽,發(fā)出HTTP Request時,在HTTP頭的 Authorization字段中放入如下的信息:
Authorization: AWS4-HMAC-SHA256
Credential=AKIDEXAMPLE/20150830/us-east-1/iam/aws4_request,
SignedHeaders=content-type;host;x-amz-date,
Signature=5d672d79c15b13162d9279b0855cfba6789a8edb4c82c400e06b5924a6f2b5d7
其中的 AKIDEXAMPLE
是 AWS Access Key ID总处, 也就是所謂的 AppID狈惫,服務(wù)器端會根據(jù)這個AppID來查相關(guān)的 Secret Access Key,然后再驗證簽名鹦马。如果胧谈,你對這個過程有點沒看懂的話,你可以讀一讀這篇文章——《Amazon S3 Rest API with curl》這篇文章里有好些代碼荸频,代碼應(yīng)該是最有細(xì)節(jié)也是最準(zhǔn)確的了菱肖。
這種認(rèn)證的方式好處在于,AppID和AppSecretKey旭从,是由服務(wù)器的系統(tǒng)開出的稳强,所以场仲,是可以被管理的,AWS的IAM就是相關(guān)的管理退疫,其管理了用戶渠缕、權(quán)限和其對應(yīng)的AppID和AppSecretKey。但是不好的地方在于褒繁,這個東西沒有標(biāo)準(zhǔn) 亦鳞,所以,各家的實現(xiàn)很不一致棒坏。比如: Acquia 的 HMAC燕差,微信的簽名算法 (這里,我們需要說明一下坝冕,微信的API沒有遵循HTTP協(xié)議的標(biāo)準(zhǔn)谁不,把認(rèn)證信息放在HTTP 頭的 Authorization
里,而是放在body里)
JWT – JSON Web Tokens
JWT是一個比較標(biāo)準(zhǔn)的認(rèn)證解決方案徽诲,這個技術(shù)在Java圈里應(yīng)該用的是非常普遍的刹帕。JWT簽名也是一種MAC(Message Authentication Code)的方法。JWT的簽名流程一般是下面這個樣:
用戶使用用戶名和口令到認(rèn)證服務(wù)器上請求認(rèn)證谎替。
認(rèn)證服務(wù)器驗證用戶名和口令后偷溺,以服務(wù)器端生成JWT Token,這個token的生成過程如下:
認(rèn)證服務(wù)器還會生成一個 Secret Key(密鑰)
對JWT Header和 JWT Payload分別求Base64钱贯。在Payload可能包括了用戶的抽象ID和的過期時間挫掏。
用密鑰對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)的請求秩命。這個JWT Token就像一個臨時用戶權(quán)證一樣尉共。
當(dāng)應(yīng)用服務(wù)器收到請求后:
- 應(yīng)用服務(wù)會檢查 JWT Token,確認(rèn)簽名是正確的弃锐。
- 然而袄友,因為只有認(rèn)證服務(wù)器有這個用戶的Secret Key(密鑰),所以霹菊,應(yīng)用服務(wù)器得把JWT Token傳給認(rèn)證服務(wù)器剧蚣。
- 認(rèn)證服務(wù)器通過JWT Payload 解出用戶的抽象ID,然后通過抽象ID查到登錄時生成的Secret Key旋廷,然后再來檢查一下簽名鸠按。
- 認(rèn)證服務(wù)器檢查通過后,應(yīng)用服務(wù)就可以認(rèn)為這是合法請求了饶碘。
我們可以看以目尖,上面的這個過程,是在認(rèn)證服務(wù)器上為用戶動態(tài)生成 Secret Key的扎运,應(yīng)用服務(wù)在驗簽的時候瑟曲,需要到認(rèn)證服務(wù)器上去簽募书,這個過程增加了一些網(wǎng)絡(luò)調(diào)用,所以测蹲,JWT除了支持HMAC-SHA256的算法外,還支持RSA的非對稱加密的算法鬼吵。
使用RSA非對稱算法扣甲,在認(rèn)證服務(wù)器這邊放一個私鑰,在應(yīng)用服務(wù)器那邊放一個公鑰齿椅,認(rèn)證服務(wù)器使用私鑰加密琉挖,應(yīng)用服務(wù)器使用公鑰解密,這樣一來涣脚,就不需要應(yīng)用服務(wù)器向認(rèn)證服務(wù)器請求了示辈,但是,RSA是一個很慢的算法遣蚀,所以矾麻,雖然你省了網(wǎng)絡(luò)調(diào)用,但是卻費了CPU芭梯,尤其是Header和Payload比較長的時候险耀。所以,一種比較好的玩法是玖喘,如果我們把header 和 payload簡單地做SHA256甩牺,這會很快,然后累奈,我們用RSA加密這個SHA256出來的字符串贬派,這樣一來,RSA算法就比較快了澎媒,而我們也做到了使用RSA簽名的目的逃默。
最后痹换,我們只需要使用一個機制在認(rèn)證服務(wù)器和應(yīng)用服務(wù)器之間定期地?fù)Q一下公鑰私鑰對就好了。
這里強烈建議全文閱讀 Anglar 大學(xué)的 《JSW:The Complete Guide to JSON Web Tokens》
OAuth 1.0
OAuth也是一個API認(rèn)證的協(xié)議,這個協(xié)議最初在2006年由Twitter的工程師在開發(fā)OpenID實現(xiàn)的時候和社交書簽網(wǎng)站Ma.gnolia時發(fā)現(xiàn)驮俗,沒有一種好的委托授權(quán)協(xié)議,后來在2007年成立了一個OAuth小組县貌,知道這個消息后抖拴,Google員工也加入進來,并完善有善了這個協(xié)議缘缚,在2007年底發(fā)布草案勾笆,過一年后,在2008年將OAuth放進了IETF作進一步的標(biāo)準(zhǔn)化工作桥滨,最后在2010年4月窝爪,正式發(fā)布OAuth 1.0弛车,即:RFC 5849 (這個RFC比起TCP的那些來說讀起來還是很輕松的),不過蒲每,如果你想了解其前身的草案纷跛,可以讀一下 OAuth Core 1.0 Revision A ,我在下面做個大概的描述邀杏。
根據(jù)RFC 5849贫奠,可以看到 OAuth 的出現(xiàn),目的是為了望蜡,用戶為了想使用一個第三方的網(wǎng)絡(luò)打印服務(wù)來打印他在某網(wǎng)站上的照片唤崭,但是,用戶不想把自己的用戶名和口令交給那個第三方的網(wǎng)絡(luò)打印服務(wù)脖律,但又想讓那個第三方的網(wǎng)絡(luò)打印服務(wù)來訪問自己的照片谢肾,為了解決這個授權(quán)的問題,OAuth這個協(xié)議就出來了小泉。
這個協(xié)議有三個角色:
User(照片所有者-用戶)
Consumer(第三方照片打印服務(wù))
Service Provider(照片存儲服務(wù))
這個協(xié)義有三個階段:
Consumer獲取Request Token
Service Provider 認(rèn)證用戶并授權(quán)Consumer
Consumer獲取Access Token調(diào)用API訪問用戶的照片
整個授權(quán)過程是這樣的:
Consumer(第三方照片打印服務(wù))需要先上Service Provider獲得開發(fā)的 Consumer Key 和 Consumer Secret
當(dāng) User 訪問 Consumer 時芦疏,Consumer 向 Service Provide 發(fā)起請求請求Request Token (需要對HTTP請求簽名)
Service Provide 驗明 Consumer 是注冊過的第三方服務(wù)商后,返回 Request Token(oauth_token)和 Request Token Secret (oauth_token_secret)
Consumer 收到 Request Token 后微姊,使用HTTP GET 請求把 User 切到 Service Provide 的認(rèn)證頁上(其中帶上Request Token)眯分,讓用戶輸入他的用戶和口令。
Service Provider 認(rèn)證 User 成功后柒桑,跳回 Consumer弊决,并返回 Request Token (oauth_token)和 Verification Code(oauth_verifier)
接下來就是簽名請求,用Request Token 和 Verification Code 換取 Access Token (oauth_token)和 Access Token Secret (oauth_token_secret)
最后使用Access Token 訪問用戶授權(quán)訪問的資源魁淳。
下圖附上一個Yahoo!的流程圖可以看到整個過程的相關(guān)細(xì)節(jié)飘诗。
因為上面這個流程有三方:User,Consumer 和 Service Provide界逛,所以昆稿,又叫 3-legged flow,三腳流程息拜。OAuth 1.0 也有不需要用戶參與的溉潭,只有Consumer 和 Service Provider 的, 也就是 2-legged flow 兩腳流程少欺,其中省掉了用戶認(rèn)證的事喳瓣。整個過程如下所示:
Consumer(第三方照片打印服務(wù))需要先上Service Provider獲得開發(fā)的 Consumer Key 和 Consumer Secret
Consumer 向 Service Provide 發(fā)起請求請求Request Token (需要對HTTP請求簽名)
Service Provide 驗明 Consumer 是注冊過的第三方服務(wù)商后,返回 Request Token(oauth_token)和 Request Token Secret (oauth_token_secret)
Consumer 收到 Request Token 后赞别,直接換取 Access Token (oauth_token)和 Access Token Secret (oauth_token_secret)
最后使用Access Token 訪問用戶授權(quán)訪問的資源畏陕。
最后,再來說一說OAuth中的簽名仿滔。
我們可以看到惠毁,有兩個密鑰犹芹,一個是Consumer注冊Service Provider時由Provider頒發(fā)的 Consumer Secret,另一個是 Token Secret鞠绰。
簽名密鑰就是由這兩具密鑰拼接而成的腰埂,其中用 &作連接符。假設(shè) Consumer Secret 為 j49sk3j29djd 而 Token Secret 為dh893hdasih9那個蜈膨,簽名密鑰為:j49sk3j29djd&dh893hdasih9
在請求Request/Access Token的時候需要對整個HTTP請求進行簽名(使用HMAC-SHA1和HMAC-RSA1簽名算法)屿笼,請求頭中需要包括一些OAuth需要的字段,如:
Consumer Key : 也就是所謂的AppID
Token: Request Token 或 Access Token
Signature Method :簽名算法比如:HMAC-SHA1
Timestamp:過期時間
Nonce:隨機字符串
Call Back:回調(diào)URL
下圖是整個簽名的示意圖:
圖片還是比較直觀的丈挟,我就不多解釋了。
OAuth 2.0
在前面志电,我們可以看到曙咽,從Digest Access, 到AppID+HMAC挑辆,再到JWT例朱,再到OAuth 1.0,這些個API認(rèn)證都是要向Client發(fā)一個密鑰(或是用密碼)然后用HASH或是RSA來簽HTTP的請求鱼蝉,這其中有個主要的原因是洒嗤,以前的HTTP是明文傳輸,所以魁亦,在傳輸過程中很容易被篡改渔隶,于是才搞出來一套的安全簽名機制,所以洁奈,這些個認(rèn)證的玩法是可以在HTTP明文協(xié)議下玩的间唉。
這種使用簽名方式大家可以看到是比較復(fù)雜的,所以利术,對于開發(fā)者來說呈野,也是很不友好的,在組織簽名的那些HTTP報文的時候印叁,各種被冒,URLEncode和Base64,還要對Query的參數(shù)進行排序轮蜕,然后有的方法還要層層簽名昨悼,非常容易出錯,另外跃洛,這種認(rèn)證的安全粒度比較粗幔戏,授權(quán)也比較單一,對于有終端用戶參與的移動端來說也有點不夠税课。所以闲延,在2012年的時候痊剖,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,國內(nèi)的微信和支付寶也支持使用OAuth 2.0寸莫。
下面捺萌,我們來重點看一下OAuth 2.0的兩個主要的Flow:
- 一個是Authorization Code Flow, 這個是 3 legged 的
- 一個是Client Credential Flow膘茎,這個是 2 legged 的桃纯。
Authorization Code Flow
Authorization Code 是最常使用的OAuth 2.0的授權(quán)許可類型,它適用于用戶給第三方應(yīng)用授權(quán)訪問自己信息的場景披坏。這個Flow也是OAuth 2.0四個Flow中我個人覺得最完整的一個Flow态坦,其流程圖如下所示。
下面是對這個流程的一個細(xì)節(jié)上的解釋:
1)當(dāng)用戶(Resource Owner)訪問第三方應(yīng)用(Client)的時候棒拂,第三方應(yīng)用會把用戶帶到認(rèn)證服務(wù)器(Authorization Server)上去伞梯,主要請求的是 /authorize API,其中的請求方式如下所示帚屉。
https://login.authorization-server.com/authorize?
client_id=6731de76-14a6-49ae-97bc-6eba6914391e
&response_type=code
&redirect_uri=http%3A%2F%2Fexample-client.com%2Fcallback%2F
&scope=read
&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 是一個隨機的字符串涮阔,主要用于防CSRF攻擊猜绣。
2)當(dāng)Authorization Server收到這個URL請求后,其會通過 client_id來檢查 redirect_uri和 scope是否合法敬特,如果合法掰邢,則彈出一個頁面,讓用戶授權(quán)(如果用戶沒有登錄伟阔,則先讓用戶登錄辣之,登錄完成后,出現(xiàn)授權(quán)訪問頁面)皱炉。
3)當(dāng)用戶授權(quán)同意訪問以后怀估,Authorization Server 會跳轉(zhuǎn)回 Client ,并以其中加入一個 Authorization Code。 如下所示:
https://example-client.com/callback?
code=Yzk5ZDczMzRlNDEwYlrEqdFSBzjqfTG
&state=xcoiv98CoolShell3kch
我們可以看到多搀,
請流動的鏈接是第 1)步中的 redirect_uri
其中的 state 的值也和第 1)步的 state一樣歧蕉。
4)接下來,Client 就可以使用 Authorization Code 獲得 Access Token康铭。其需要向 Authorization Server 發(fā)出如下請求惯退。
POST /oauth/token HTTP/1.1
Host: authorization-server.com
code=Yzk5ZDczMzRlNDEwYlrEqdFSBzjqfTG
&grant_type=code
&redirect_uri=https%3A%2F%2Fexample-client.com%2Fcallback%2F
&client_id=6731de76-14a6-49ae-97bc-6eba6914391e
&client_secret=JqQX2PNo9bpM0uEihUPzyrh
5)如果沒什么問題,Authorization 會返回如下信息从藤。
{
"access_token": "iJKV1QiLCJhbGciOiJSUzI1NiI",
"refresh_token": "1KaPlrEqdFSBzjqfTGAMxZGU",
"token_type": "bearer",
"expires": 3600,
"id_token": "eyJ0eXAiOiJKV1QiLCJhbGciO.eyJhdWQiOiIyZDRkM..."
}
其中催跪,
access_token就是訪問請求令牌了
refresh_token用于刷新 access_token
id_token 是JWT的token,其中一般會包含用戶的OpenID
6)接下來就是用 Access Token 請求用戶的資源了夷野。
GET /v1/user/pictures
Host: https://example.resource.com
Authorization: Bearer iJKV1QiLCJhbGciOiJSUzI1NiI
Client Credential Flow
Client Credential 是一個簡化版的API認(rèn)證懊蒸,主要是用于認(rèn)證服務(wù)器到服務(wù)器的調(diào)用,也就是沒有用戶參與的的認(rèn)證流程悯搔。下面是相關(guān)的流程圖骑丸。
這個過程非常簡單,本質(zhì)上就是Client用自己的 client_id和 client_secret向Authorization Server 要一個 Access Token鳖孤,然后使用Access Token訪問相關(guān)的資源者娱。
請求示例
POST /token HTTP/1.1
Host: server.example.com
Content-Type: application/x-www-form-urlencoded
grant_type=client_credentials
&client_id=czZCaGRSa3F0Mzpn
&client_secret=7Fjfp0ZBr1KtDRbnfVdmIw
返回示例
{
"access_token":"MTQ0NjJkZmQ5OTM2NDE1ZTZjNGZmZjI3",
"token_type":"bearer",
"expires_in":3600,
"refresh_token":"IwOGYzYTlmM2YxOTQ5MGE3YmNmMDFkNTVk",
"scope":"create"
}
這里抡笼,容我多扯一句苏揣,微信公從平臺的開發(fā)文檔中,使用了OAuth 2.0 的 Client Credentials的方式(參看文檔“微信公眾號獲取access token”)推姻,我截了個圖如下所謂平匈。我們可以看到,微信公眾號使用的是GET方式的請求藏古,把AppID和AppSecret放在了URL中增炭,雖然這也符合OAuth 2.0,但是并不好拧晕,因為大多數(shù)網(wǎng)關(guān)代理會把整個URI請求記到日志中隙姿。我們只要腦補一下騰訊的網(wǎng)關(guān)的Access Log,里面的日志一定會有很多的各個用戶的AppID和AppSecret……
小結(jié)
講了這么多厂捞,我們來小結(jié)一下(下面的小結(jié)可能會有點散)
兩個概念和三個術(shù)語
區(qū)分兩個概念:Authentication(認(rèn)證) 和 Authorization (授權(quán))输玷,前者是證明請求者是身份,就像身份證一樣靡馁,后者是為了獲得權(quán)限欲鹏。身份是區(qū)別于別人的證明,而權(quán)限是證明自己的特權(quán)臭墨。Authentication為了證明操作的這個人就是他本人赔嚎,需要提供密碼、短信驗證碼,甚至人臉識別尤误。Authorization 則是不需要在所有的請求都需要驗人侠畔,是在經(jīng)過Authorization后得到一個Token,這就是Authorization袄膏。就像護照和簽證一樣践图。
區(qū)分三個概念:編碼Base64Encode、簽名HMAC沉馆、加密RSA码党。編碼是為了更的傳輸,等同于明文斥黑,簽名是為了信息不能被篡改揖盘,加密是為了不讓別人看到是什么信息。
明白一些初衷
使用復(fù)雜地HMAC哈希簽名方式主要是應(yīng)對當(dāng)年沒有TLS/SSL加密鏈路的情況锌奴。
JWT把 uid 放在 Token中目的是為了去掉狀態(tài)兽狭,但不能讓用戶修改,所以需要簽名鹿蜀。
OAuth 1.0區(qū)分了兩個事箕慧,一個是第三方的Client,一個是真正的用戶茴恰,其先拿Request Token颠焦,再換Access Token的方法主要是為了把第三方應(yīng)用和用戶區(qū)分開來。
用戶的Password是用戶自己設(shè)置的往枣,復(fù)雜度不可控伐庭,服務(wù)端頒發(fā)的Serect會很復(fù)雜,但主要目的是為了容易管理分冈,可以隨時注銷掉圾另。
OAuth 協(xié)議有比所有認(rèn)證協(xié)議有更為靈活完善的配置,如果使用AppID/AppSecret簽名的方式雕沉,又需要做到可以有不同的權(quán)限和可以隨時注銷集乔,那么你得開發(fā)一個像AWS的IAM這樣的賬號和密鑰對管理的系統(tǒng)。
相關(guān)的注意事項
無論是哪種方式坡椒,我們都應(yīng)該遵循HTTP的規(guī)范扰路,把認(rèn)證信息放在 Authorization HTTP 頭中。
不要使用GET的方式在URL中放入secret之類的東西肠牲,因為很多proxy或gateway的軟件會把整個URL記在Access Log文件中幼衰。
密鑰Secret相當(dāng)于Password,但他是用來加密的缀雳,最好不要在網(wǎng)絡(luò)上傳輸渡嚣,如果要傳輸,最好使用TLS/SSL的安全鏈路。
HMAC中無論是MD5還是SHA1/SHA2识椰,其計算都是非尘希快的,RSA的非對稱加密是比較耗CPU的腹鹉,尤其是要加密的字符串很長的時候藏畅。
最好不要在程序中hard code 你的 Secret,因為在github上有很多黑客的軟件在監(jiān)視各種Secret功咒,千萬小心愉阎!這類的東西應(yīng)該放在你的配置系統(tǒng)或是部署系統(tǒng)中,在程序啟動時設(shè)置在配置文件或是環(huán)境變量中力奋。
使用AppID/AppSecret榜旦,還是使用OAuth1.0a,還是OAuth2.0景殷,還是使用JWT溅呢,我個人建議使用TLS/SSL下的OAuth 2.0。
密鑰是需要被管理的猿挚,管理就是可以新增可以撤銷咐旧,可以設(shè)置賬戶和相關(guān)的權(quán)限。最好密鑰是可以被自動更換的绩蜻。
認(rèn)證授權(quán)服務(wù)器(Authorization Server)和應(yīng)用服務(wù)器(App Server)最好分開铣墨。
(全文完)
轉(zhuǎn)自 https://coolshell.cn/articles/19395.html