轉(zhuǎn)自 https://coolshell.cn/articles/19395.html
我們知道,HTTP是無狀態(tài)的,所以,當我們需要獲得用戶是否在登錄的狀態(tài)時,我們需要檢查用戶的登錄狀態(tài),一般來說,用戶的登錄成功后,服務(wù)器會發(fā)一個登錄憑證(又被叫作Token),就像你去訪問某個公司,在前臺被認證過合法后,這個公司的前臺會給你的一個訪客卡一樣,之后,你在這個公司內(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認證技術(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認證技術(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ù)端如果沒有在頭里看到認證字段,則返回401錯,以及一個個
[WWW-Authenticate](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/WWW-Authenticate): Basic Realm='HelloWorld'
之類的頭要求客戶端進行認證。之后如果沒有認證通過,則返回一個401錯。如果服務(wù)端認證通過,那么會返回200。
我們可以看到,使用Base64的目的無非就是為了把一些特殊的字符給搞掉,這樣就可以放在HTTP協(xié)議里傳輸了。而這種方式的問題最大的問題就是把用戶名和口令放在網(wǎng)絡(luò)上傳,所以,一般要配合TLS/SSL的安全加密方式來使用。我們可以看到 JIRA Cloud 的API認證支持HTTP Basic 這樣的方式。
但我們還是要知道,這種把用戶名和密碼同時放在公網(wǎng)上傳輸?shù)姆绞接悬c不太好,因為Base64不是加密協(xié)議,而是編碼協(xié)議,所以就算是有HTTPS作為安全保護,給人的感覺還是不放心。
Digest Access
中文稱“HTTP 摘要認證”,最初被定義在了 RFC 2069 文檔中(后來被 RFC 2617 引入了一系列安全增強的選項;“保護質(zhì)量”(qop)、隨機數(shù)計數(shù)器由客戶端增加、以及客戶生成的隨機數(shù))。
其基本思路是,請求方把用戶名口令和域做一個MD5 – MD5(username:realm:password)
然后傳給服務(wù)器,這樣就不會在網(wǎng)上傳用戶名和口令了,但是,因為用戶名和口令基本不會變,所以,這個MD5的字符串也是比較固定的,因此,這個認證過程在其中加入了兩個事,一個是 nonce
另一個是 qop
- 首先,調(diào)用方發(fā)起一個普通的HTTP請求。比如:
GET /coolshell/admin/ HTTP/1.1
- 服務(wù)端自然不能認證能過,服務(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 詞條非常詳細地描述了這個細節(jié)。
摘要認證這個方式會比之前的方式要好一些,因為沒有在網(wǎng)上傳遞用戶的密碼,而只是把密碼的MD5傳送過去,相對會比較安全,而且,其并不需要是否TLS/SSL的安全鏈接。但是,別看這個算法這么復(fù)雜,最后你可以發(fā)現(xiàn),整個過程其實關(guān)鍵是用戶的password,這個password如果不夠得雜,其實是可以被暴力破解的,而且,整個過程是非常容易受到中間人攻擊——比如一個中間人告訴客戶端需要的 Basic 的認證方式 或是 老舊簽名認證方式(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的,就像我們每個人的身份證一樣,只是用來標注不同的人,不是用來做身份認證的。與前面的不同之處是,這里,我們需要用App ID 來映射一個用于加密的密鑰,這樣一來,我們就可以在服務(wù)器端進行相關(guān)的管理,我們可以生成若干個密鑰對(AppID, AppSecret),并可以有更細粒度的操作權(quán)限管理。
把AppID和HMAC用于API認證,目前來說,玩得最好最專業(yè)的應(yīng)該是AWS了,我們可以通過S3的API請求簽名文檔看到AWS是怎么玩的。整個過程還是非常復(fù)雜的,可以通過下面的圖片流程看個大概。基本上來說,分成如下幾個步驟:
- 把HTTP的請求(方法、URI、查詢字串、頭、簽名頭,body)打個包叫
CanonicalRequest
,作個SHA-256的簽名,然后再做一個base16的編碼 - 把上面的這個簽名和簽名算法
AWS4-HMAC-SHA256
、時間戳、Scop,再打一個包,叫StringToSign
。 - 準備簽名,用
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)該是最有細節(jié)也是最準確的了。
這種認證的方式好處在于,AppID和AppSecretKey,是由服務(wù)器的系統(tǒng)開出的,所以,是可以被管理的,AWS的IAM就是相關(guān)的管理,其管理了用戶、權(quán)限和其對應(yīng)的AppID和AppSecretKey。但是不好的地方在于,這個東西沒有標準 ,所以,各家的實現(xiàn)很不一致。比如: Acquia 的 HMAC,微信的簽名算法 (這里,我們需要說明一下,微信的API沒有遵循HTTP協(xié)議的標準,把認證信息放在HTTP 頭的 Authorization
里,而是放在body里)
JWT – JSON Web Tokens
JWT是一個比較標準的認證解決方案,這個技術(shù)在Java圈里應(yīng)該用的是非常普遍的。JWT簽名也是一種MAC(Message Authentication Code)的方法。JWT的簽名流程一般是下面這個樣:
用戶使用用戶名和口令到認證服務(wù)器上請求認證。
認證服務(wù)器驗證用戶名和口令后,以服務(wù)器端生成JWT Token,這個token的生成過程如下:
認證服務(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)證一樣。
當應(yīng)用服務(wù)器收到請求后:
- 應(yīng)用服務(wù)會檢查 JWT Token,確認簽名是正確的。
- 然而,因為只有認證服務(wù)器有這個用戶的Secret Key(密鑰),所以,應(yīng)用服務(wù)器得把JWT Token傳給認證服務(wù)器。
- 認證服務(wù)器通過JWT Payload 解出用戶的抽象ID,然后通過抽象ID查到登錄時生成的Secret Key,然后再來檢查一下簽名。
- 認證服務(wù)器檢查通過后,應(yīng)用服務(wù)就可以認為這是合法請求了。
我們可以看以,上面的這個過程,是在認證服務(wù)器上為用戶動態(tài)生成 Secret Key的,應(yīng)用服務(wù)在驗簽的時候,需要到認證服務(wù)器上去簽,這個過程增加了一些網(wǎng)絡(luò)調(diào)用,所以,JWT除了支持HMAC-SHA256的算法外,還支持RSA的非對稱加密的算法。
使用RSA非對稱算法,在認證服務(wù)器這邊放一個私鑰,在應(yīng)用服務(wù)器那邊放一個公鑰,認證服務(wù)器使用私鑰加密,應(yīng)用服務(wù)器使用公鑰解密,這樣一來,就不需要應(yīng)用服務(wù)器向認證服務(wù)器請求了,但是,RSA是一個很慢的算法,所以,雖然你省了網(wǎng)絡(luò)調(diào)用,但是卻費了CPU,尤其是Header和Payload比較長的時候。所以,一種比較好的玩法是,如果我們把header 和 payload簡單地做SHA256,這會很快,然后,我們用RSA加密這個SHA256出來的字符串,這樣一來,RSA算法就比較快了,而我們也做到了使用RSA簽名的目的。
最后,我們只需要使用一個機制在認證服務(wù)器和應(yīng)用服務(wù)器之間定期地換一下公鑰私鑰對就好了。
這里強烈建議全文閱讀 Anglar 大學(xué)的 《JSW:The Complete Guide to JSON Web Tokens》
OAuth 1.0
OAuth也是一個API認證的協(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作進一步的標準化工作,最后在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 認證用戶并授權(quán)Consumer
Consumer獲取Access Token調(diào)用API訪問用戶的照片
整個授權(quán)過程是這樣的:
Consumer(第三方照片打印服務(wù))需要先上Service Provider獲得開發(fā)的 Consumer Key 和 Consumer Secret
當 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 的認證頁上(其中帶上Request Token),讓用戶輸入他的用戶和口令。
Service Provider 認證 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)細節(jié)。
因為上面這個流程有三方:User,Consumer 和 Service Provide,所以,又叫 3-legged flow,三腳流程。OAuth 1.0 也有不需要用戶參與的,只有Consumer 和 Service Provider 的, 也就是 2-legged flow 兩腳流程,其中省掉了用戶認證的事。整個過程如下所示:
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認證都是要向Client發(fā)一個密鑰(或是用密碼)然后用HASH或是RSA來簽HTTP的請求,這其中有個主要的原因是,以前的HTTP是明文傳輸,所以,在傳輸過程中很容易被篡改,于是才搞出來一套的安全簽名機制,所以,這些個認證的玩法是可以在HTTP明文協(xié)議下玩的。
這種使用簽名方式大家可以看到是比較復(fù)雜的,所以,對于開發(fā)者來說,也是很不友好的,在組織簽名的那些HTTP報文的時候,各種,URLEncode和Base64,還要對Query的參數(shù)進行排序,然后有的方法還要層層簽名,非常容易出錯,另外,這種認證的安全粒度比較粗,授權(quán)也比較單一,對于有終端用戶參與的移動端來說也有點不夠。所以,在2012年的時候,OAuth 2.0 的 RFC 6749 正式放出。
OAuth 2.0依賴于TLS/SSL的鏈路加密技術(shù)(HTTPS),完全放棄了簽名的方式,認證服務(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,其流程圖如下所示。
下面是對這個流程的一個細節(jié)上的解釋:
1)當用戶(Resource Owner)訪問第三方應(yīng)用(Client)的時候,第三方應(yīng)用會把用戶帶到認證服務(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為告訴認證服務(wù)器,我要走Authorization Code Flow。
redirect_uri意思是我跳轉(zhuǎn)回第三方應(yīng)用的URL
scope意是相關(guān)的權(quán)限
state 是一個隨機的字符串,主要用于防CSRF攻擊。
2)當Authorization Server收到這個URL請求后,其會通過 client_id來檢查 redirect_uri和 scope是否合法,如果合法,則彈出一個頁面,讓用戶授權(quán)(如果用戶沒有登錄,則先讓用戶登錄,登錄完成后,出現(xiàn)授權(quán)訪問頁面)。
3)當用戶授權(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認證,主要是用于認證服務(wù)器到服務(wù)器的調(diào)用,也就是沒有用戶參與的的認證流程。下面是相關(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(認證) 和 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)對當年沒有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é)議有比所有認證協(xié)議有更為靈活完善的配置,如果使用AppID/AppSecret簽名的方式,又需要做到可以有不同的權(quán)限和可以隨時注銷,那么你得開發(fā)一個像AWS的IAM這樣的賬號和密鑰對管理的系統(tǒng)。
相關(guān)的注意事項
無論是哪種方式,我們都應(yīng)該遵循HTTP的規(guī)范,把認證信息放在 Authorization HTTP 頭中。
不要使用GET的方式在URL中放入secret之類的東西,因為很多proxy或gateway的軟件會把整個URL記在Access Log文件中。
密鑰Secret相當于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)限。最好密鑰是可以被自動更換的。
認證授權(quán)服務(wù)器(Authorization Server)和應(yīng)用服務(wù)器(App Server)最好分開。
(全文完)
轉(zhuǎn)自 https://coolshell.cn/articles/19395.html