Basic Authentication
basic是最簡單的認證機制,客戶端向伺服器端請求資料時,如果未被認證,伺服器會向客戶端傳送驗證請求.
HTTP/1.0 401 Unauthorised
Server: SokEvo/1.0
WWW-Authenticate: Basic realm=”google.com”
Content-Type: text/html
Content-Length: xxx
當客戶端收到401返回值時,將自動彈出一個登陸視窗,要求使用者輸入使用者名稱和密碼.
使用者輸入使用者名稱和密碼後,經過瀏覽器base64編碼後,再次傳送請求
Get /index.html HTTP/1.0
Host:www.google.com
Authorization: Basic d2FuZzp3YW5n
伺服器收到請求後將使用者資訊取出解密,對比驗證.
session-cookie
session
是快取在服務端的,cookie
則是快取在客戶端,他們都由服務端生成,為了彌補 Http
協議無狀態的缺陷。
- 伺服器在接受客戶端首次訪問時在伺服器端建立seesion,然後儲存seesion(我們可以將seesion儲存在記憶體中,也可以儲存在redis中,推薦使用後者),然後給這個session生成一個唯一的標識字串,然後在 響應頭中種下這個唯一標識字串。
- 簽名。這一步通過祕鑰對sid進行簽名處理,避免客戶端修改sid。(非必需步驟)
- 瀏覽器中收到請求響應的時候會解析響應頭,然後將sid儲存在本地cookie中,瀏覽器在下次http請求的請求頭中會帶上該域名下的cookie資訊。
- 伺服器在接受客戶端請求時會去解析請求頭cookie中的sid,然後根據這個sid去找伺服器端儲存的該客戶端的session,然後判斷該請求是否合法。
Token
一般 token
由使用者資訊、時間戳和由 hash
演算法加密的簽名構成。
- 客戶端使用使用者名稱跟密碼請求登入
- 服務端收到請求,去驗證使用者名稱與密碼
- 驗證成功後,服務端會簽發一個
Token
,再把這個Token
傳送給客戶端 - 客戶端收到
Token
以後可以把它儲存起來,比如放在Cookie
裡或者Local Storage
裡 - 客戶端每次向服務端請求資源的時候需要帶著服務端簽發的
Token
- 服務端收到請求,然後去驗證客戶端請求裡面帶著的
Token
(request頭部新增Authorization),如果驗證成功,就向客戶端返回請求的資料 ,如果不成功返回401錯誤碼,鑑權失敗。
token認證可以支援多種客戶端,而不僅是瀏覽器,且不受同源策略的影響.
基於token的解決方案有很多,常用的是JWT.JWT
的原理是,伺服器認證以後,生成一個 JSON
物件,這個 JSON
物件肯定不能裸傳給使用者,那誰都可以篡改這個物件傳送請求。因此這個 JSON
物件會被伺服器端簽名加密後返回給使用者,返回的內容就是一張令牌,以後使用者每次訪問伺服器端就帶著這張令牌
JWT由三部分組成: Header.Payload.Signature. Header存放後設資料、Payload存放實際傳遞的資料,因為是Base64編碼的,所以不能存放祕密資訊.Signature是前兩個部分的簽名,防止資料篡改.
特點:
- JWT 預設是不加密,但也是可以加密的。生成原始 Token 以後,可以用金鑰再加密一次。
- JWT 不加密的情況下,不能將祕密資料寫入 JWT。
- JWT 不僅可以用於認證,也可以用於交換資訊。有效使用 JWT,可以降低伺服器查詢資料庫的次數。
- JWT 的最大缺點是,由於伺服器不儲存 session 狀態,因此無法在使用過程中廢止某個 token,或者更改 token 的許可權。也就是說,一旦 JWT 簽發了,在到期之前就會始終有效,除非伺服器部署額外的邏輯。
- JWT 本身包含了認證資訊,一旦洩露,任何人都可以獲得該令牌的所有許可權。為了減少盜用,JWT 的有效期應該設定得比較短。對於一些比較重要的許可權,使用時應該再次對使用者進行認證。
- 為了減少盜用,JWT 不應該使用 HTTP 協議明碼傳輸,要使用 HTTPS 協議傳輸。
OAuth2.0
OAuth 2.0是目前最流行的授權機制,用來授權第三方應用,獲取使用者資料.
資料所有者告訴系統,同意第三方應用進入系統,獲取這些資料.系統從而產生一個短期的進入令牌用來代替密碼,供第三方應用使用.
令牌是短期有效的,可隨時被資料所有者撤銷,並且有許可權範圍. 以上特點保證了令牌既可以讓第三方應用獲得許可權,同時又隨時可控,不會危機系統安全.
四種授權方式
OAuth引入了一個授權層,用來分離兩種不同的角色: 客戶端和資源所有者.資源所有者同意後,資源伺服器向客戶端頒發令牌,客戶端通過令牌去請求資料.資源伺服器會與對應的授權伺服器通訊、校驗令牌來確定客戶端是否可以訪問資源.
OAuth的核心就是向第三方應用辦法令牌, OAuth規定了四種獲得令牌的流程:
-
授權碼 (authorization-code): 第三方應用先申請一個授權碼, 然後再用該碼獲取令牌
這種方式是最常用的流程,安全性也最高,它適用於那些有後端的 Web 應用。授權碼通過前端傳送,令牌則是儲存在後端,而且所有與資源伺服器的通訊都在後端完成。這樣的前後端分離,可以避免令牌洩漏。
A網站想要得到B網站的令牌,就會提供B網站的帶引數連結,使用者跳轉到B網站,B網站會要求使用者登陸,然後詢問"A 網站要求獲得 xx 許可權,你是否同意?.
Response_type表示要求返回授權碼
Client_id 讓B知道是誰在請求(一個應用要求 OAuth 授權,必須先到對方網站登記,讓對方知道是誰在請求。然後該網站會返回client_id和client_secret)
Scop 表示授權範圍
https://b.com/oauth/authorize? response_type=code& client_id=CLIENT_ID& redirect_uri=CALLBACK_URL& scope=read
同意後,B網站跳回redirect_uri引數指定的網址,並附帶授權碼
https://a.com/callback?code=AUTHORIZATION_CODE
A拿到授權碼後,就可以在後端向B網站請求令牌
https://b.com/oauth/token? client_id=CLIENT_ID& client_secret=CLIENT_SECRET& grant_type=authorization_code& code=AUTHORIZATION_CODE& redirect_uri=CALLBACK_URL
B網站收到請求後,向redirect_uri傳送一段json資料, A網站在後端就拿到了.
{ "access_token":"ACCESS_TOKEN", "token_type":"bearer", "expires_in":2592000, "refresh_token":"REFRESH_TOKEN", "scope":"read", "uid":100101, "info":{...} }
-
隱藏式 (implicit)
有些 Web 應用是純前端應用,沒有後端。這時就不能用上面的方式了,必須將令牌儲存在前端。RFC 6749 就規定了第二種方式,允許直接向前端頒發令牌。這種方式沒有授權碼這個中間步驟,所以稱為(授權碼)"隱藏式"(implicit)。
https://b.com/oauth/authorize? response_type=token& client_id=CLIENT_ID& redirect_uri=CALLBACK_URL& scope=read
response_type為token表示直接返回令牌.
https://a.com/callback#token=ACCESS_TOKEN Note: #代表網頁中的一個位置,當瀏覽器讀取這個URL時,會自動將位置滾動到可視區域. 為網頁位置指定識別符號,有兩個方法。一是使用錨點,比如<a name="print"></a>,二是使用id屬性,比如<div id="print" > #是用來指導瀏覽器動作的,對伺服器端完全無用。所以,HTTP請求中不包括#。
注意,令牌的位置是 URL 錨點(fragment),而不是查詢字串(querystring),這是因為 OAuth 2.0 允許跳轉網址是 HTTP 協議,因此存在"中間人攻擊"的風險,而瀏覽器跳轉時,錨點不會發到伺服器,就減少了洩漏令牌的風險。>
這種方式把令牌直接傳給前端,是很不安全的。因此,只能用於一些安全要求不高的場景,並且令牌的有效期必須非常短,通常就是會話期間(session)有效,瀏覽器關掉,令牌就失效了。
-
密碼式 (password)
如果你高度信任某個應用,RFC 6749 也允許使用者把使用者名稱和密碼,直接告訴該應用。該應用就使用你的密碼,申請令牌,這種方式稱為"密碼式"(password)。
A網站直接要求使用者提供B網站的使用者名稱和密碼,拿到以後,A就直接向B請求令牌,B網站驗證身份後直接在http回應中給出令牌,不需要跳轉.
https://oauth.b.com/token? grant_type=password& username=USERNAME& password=PASSWORD& client_id=CLIENT_ID
-
客戶端憑證 (client credentials)
適用於沒有前端的命令列應用,即在命令列下請求令牌。
https://oauth.b.com/token? grant_type=client_credentials& client_id=CLIENT_ID& client_secret=CLIENT_SECRET
使用令牌
A 網站拿到令牌以後,就可以向 B 網站的 API 請求資料了。
此時,每個發到 API 的請求,都必須帶有令牌。具體做法是在請求的頭資訊,加上一個Authorization
欄位,令牌就放在這個欄位裡面。
curl -H "Authorization: Bearer ACCESS_TOKEN" \
"https://api.b.com"
更新令牌
OAuth 2.0 允許使用者自動更新令牌.B 網站頒發令牌的時候,一次性頒發兩個令牌,一個用於獲取資料,另一個用於獲取新的令牌(refresh token 欄位)。令牌到期前,使用者使用 refresh token 發一個請求,去更新令牌。
https://b.com/oauth/token?
grant_type=refresh_token&
client_id=CLIENT_ID&
client_secret=CLIENT_SECRET&
refresh_token=REFRESH_TOKEN