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