無論是自然資源還是網際網路上的資源,需要控制使用權與被使用權,以保護資源的安全、合理的使用和有效的管控。
專案中,我們需要控制的是使用者資源,既要保證有效使用者的合理使用,又要防範非法使用者的攻擊。如此,如何區分有效和非法就是我們需要考慮的問題,簡單點,通過賬號密碼來區分,能夠通過檢測的便是有效使用者。
可當專案越來越複雜,使用者還想使用第三方應用,那麼不能將賬號密碼交給第三方吧,同時,儘管通過賬號密碼檢測了是有效使用者,那麼是否就能夠訪問資源、使用資源呢,這也未必。
種種因素下,OAuth(Open Authorization)作為一個安全、開放且簡易的標準,方便我們在專案中協調資源、控制授權,以在使用者、第三方應用和使用者資源三者間形成一個有效的控制機制,而無需將使用者的賬號密碼提供給第三方應用,且還能控制第三方應用對資源的訪問與使用。
An open protocol to allow secure authorizationin a simple and standard method from web, mobile and desktop applications.
認證與授權
對於第三方應用想要訪問被保護的資源,應當是使用授權訪問,而這前提是要經過認證環節,畢竟認證成功,但不代表就有許可權訪問資源。OAuth更多的是側重於如何實現授權來控制第三方應用能夠訪問資源。認證協議是基於其上的Open ID Connect。儘管OAuth的流程中存在認證部分內容,但不能說OAuth就是認證協議。
- Authentication(認證) 是驗證使用者(是第三方應用的使用者)的身份的憑據(賬號和密碼),通過這個憑據,系統得以知道你就是你,也就是說系統存在你這個使用者。所以,Authentication 被稱為身份/使用者驗證。
- Authorization(授權) 發生在 Authentication(認證) 之後。其本身意思就很明瞭,它主要掌管我們訪問系統的許可權。比如有些特定資源只能具有特定的許可權才能訪問。比如手機瀏覽器想要訪問底層攝像頭,需要經使用者允許訪問才能獲得授權,以此才能獲取到具體資源。
OAuth歷史
OAuth 的歷史版本中有 OAuth 1.0 和 OAuth 2.0,其中 OAuth 1.0 在 2010 年發表為 RFC5849,OAuth 2.0 為 RFC6749,並且 OAuth 2.0 不向下相容 OAuth 1.0,目前 OAuth 2.0 被廣泛使用,因此這裡不再對 OAuth 1.0 進行更多的描述。
OAuth的幾種角色
-
Resource Owner:資源所有者,擁有資源的人,簡單說就是誰在操作瀏覽器/App/小程式..
-
Resource Server:資源伺服器,資源擁有者擁有的資源
-
Client:第三方應用,從資源所有者處獲取授權,然後從資源伺服器獲取資源
當不考慮OAuth協議,我們常見便是如上三種,想要第三方應用可以使用資源伺服器的資源,又得把自身的賬號密碼交付給第三方應用,帶來不安全。如此再加入一個角色,以平衡控制。 -
Authorization Server:授權伺服器,提供給資源所有者完成授權並確定允許給第三方應用可以使用的資源範圍與操作。由資源伺服器方負責實現。
如此一來,資源所有者將賬號密碼提供給授權伺服器來完成驗證,驗證有效由授權伺服器來負責提供能夠代表身份的標識給第三方應用,這樣一來降低了賬號密碼洩露的可能。
OAuth的工作流程
獲取授權過程:
(A) 使用者(Resource Owner)訪問第三方應用(Client),第三方應用(Client)向授權伺服器(Authorization Server)發起請求,即第三方應用(Client)向使用者(Resource Owner)請求授權。
(B)使用者(Resource Owner)輸入賬號密碼,勾選允許的資源範圍,提交表單,授權伺服器驗證後返回授權許可給第三方應用(Client)。
(C) 第三方應用(Client)再帶著授權許可請求授權伺服器(Authorization Server)換取訪問令牌(Access_token)。
(D) 授權伺服器(Authorization Server)驗證第三方應用(Client)的身份和授權許可,然後給第三方應用(Client)返回訪問令牌(Access_token)。
獲取資源過程:
(E) 第三方應用(Client)拿訪問令牌(Access_token)去請求資源伺服器(Resource Server)獲取資源。
(F) 資源伺服器(Resource Server)返回允許範圍內的資源給第三方應用(Client)。
OAuth的幾種授權方式
在增加了一個Authorization Server角色後,存在幾種常用獲取授權許可(資源所有者授權能夠訪問資源伺服器內有效範圍資源的憑據,供第三方應用來訪問資源伺服器)的方式。
- Authorization Code:授權碼
- Implicit:隱式授權
- Client Credentials:客戶端憑證授權
- Resource Owner Password Credentials:資源所有者密碼憑證授權
如上四種模式有一個前提是第三方應用需要在授權伺服器中有過登記註冊過程。不應該讓隨便一個第三方應用需要請求資源,就跳轉到授權伺服器,然後讓資源所有者完成授權,然後便可去訪問資源。而應該是有效的第三方應用,何為有效,在授權伺服器中有過登記,並返回給第三方客戶端一對識別碼,客戶端Id和客戶端金鑰,由第三方應用私有儲存,這個很是常見。
授權碼
授權碼是目前 OAuth 2.0 中最常用的。通過去授權端獲取授權碼,利用授權碼換取 token,通過使用 token 去資源伺服器獲取受保護資源。
核心部分在於紅色部分,細講這幾部分
1、當前訪問的客戶端的URL地址重定向到授權伺服器的授權地址(2.0~2.1)
https://authorization-server.com/auth
?response_type=code
&client_id=29352915982374239857
&redirect_uri=https%3A%2F%2Fexample-app.com%2Fcallback
&scope=create+delete
&state=xcoiv98y2kd22vusuye3kch
引數說明:
response_type=code
必選,向授權伺服器標識客戶端使用授權碼方式client_id
必選,應用的身份標識Id(應用先得在授權伺服器中登記得到一對ClientId和Client Secret)。redirect_uri
可選,授權成功後從授權伺服器重定向回client的地址。scope
可選,表示Client申請授權的範圍,用一個或多個空格分隔。state
推薦,Client提供的一個字串,伺服器會原樣返回給Client,以阻止CSRF攻擊。
2、授權頁中同意授權,重定向回Client提供的重定向地址(3.0~3.3)
https://example-app.com/redirect
?code=g0ZGZmNjVmOWIjNTk2NTk4ZTYyZGI3
&state=xcoiv98y2kd22vusuye3kch
引數說明:
state
為Client提供的字串,授權伺服器原樣返回,用於Client防止CSRF攻擊。code
授權伺服器生成的授權碼(臨時的)。
3、授權碼換訪問令牌(4.0~4.2)
重定向回到Client後,Client獲取到授權碼,向授權伺服器傳送Post請求,以換取訪問令牌。
POST
https://authorization-server.com/auth/token
Content-Type: application/x-www-form-urlencoded
引數:
grant_type=code
&code=Yzk5ZDczMzRlNDEwY
&redirect_uri=https://example-app.com/cb
&client_id=mRkZGFjM
&client_secret=xRsjfkgfgfdfvxsfg
引數說明:
grant_type=authorization_code
必選,向授權伺服器標識客戶端型別為授權碼型別。code
必選,上一步中重定向返回的授權碼。redirect_uri
必選,和第一步中提供的redirect_uri相同。client_id
必選,客戶端Id,第一步中提及的應用標識Id。client_secret
必選,客戶端標識Id對應金鑰,用於授權伺服器對客戶端身份驗證,並防範授權碼和客戶端Id暴露被直接用來換取訪問令牌。
授權伺服器驗證客戶端身份以及授權碼正確及有效後返回訪問令牌。
{
"access_token": "MTQ0NjJkZmQ5OTM2NDE1ZTZjNGZmZjI3",
"token_type": "bearer",
"expires_in": 3600,
"refresh_token": "IwOGYzYTlmM2YxOTQ5MGE3YmNmMDFkNTVk",
"scope": "create delete"
}
核心部分授權碼流程結束,後續便是拿著訪問令牌獲取資源。
問題解惑
雖然整個流程算是清晰了,但還是存在一些疑惑,如下所示,也隨著更多的瞭解也有了答案。純屬自問自答了。
1、授權碼是一個臨時的,用來令Client向Authorization Server換取訪問令牌(Access token),那麼這一次獲取到訪問令牌,並獲取到了資源後。User再次訪問同樣的資源,這個授權碼換取訪問令牌的過程沒有了,那麼如何從哪裡獲取訪問令牌以去訪問資源呢?
答:除了返回Access_token還返回了一個Refresh_token。Access_token有短暫的生命週期,如果過期了就用Refresh_token去重新整理或是獲取一個新的Access_token。Refresh_token的生命週期更長。
2、當一次訪問資源的流程結束後,下一次再訪問時,如何可以不要再登入了,也就是這個流程中並沒返回標識資訊到User,如何判斷User有效並可以不用重定向到使用者登入頁?
答:該部分標識使用者身份屬於OIDC的部分與OAuth無關,OAuth目的是訪問受保護的資源。OIDC中有個ID token,一般為JWT格式,用於返回給前端,然後請求時攜帶著以標識使用者身份。又或者是採用Cookie+Session方式來標識使用者。具體點就是從AuthServer重定向到Client時已經帶上了身份標識資訊。
3、Refresh_token是需要Client端存起來嗎,如何標識這個Client端對應是哪個User?
答:標識Client端對應的是哪個User,這部分不是OAuth2.0協議所關注的,這部分更多是OIDC協議的事情。至於Refresh_token的儲存,如下方式可以。
- 如果User和Client間是Cookie和Session,那麼可以通過將標識寫入到Cookie中,同時本地Session中記錄UserId和Refresh_token,這是可以做到的,由Cookie和Session來標識使用者是誰。
- 如果是使用Jwt,那麼或許是存在Cache(Memory、Redis)或是直接存在資料表中,因為已經知道了UserId以及Refresh_token的有效期,這些都可以儲存起來完成對應關係。
需要注意
- Access_token和Refresh_token其核心在於,Client端請求資源的訪問令牌對外隱藏,只在Client端、Authorization Server和Resource Server間使用。
- 標識這個使用者身份以及如何確保使用者下次訪問仍是這個使用者並不是OAuth2.0所關心的,OAuth2.0更多是偏向於對保護資源的有效訪問,核心部分聚焦於此。
隱式授權
在授權碼的環節上做了一些簡化,原有返回授權碼的過程變成了直接返回訪問令牌。這種情況在純前端Web應用中(如SPA、瀏覽器外掛)常出現,沒有後臺,在瀏覽器中需要負責完成所有過程。
相比於授權碼形式,少了Client Server與Authorization Server完成交換Access Token過程,而是直接將返回授權碼替換成了返回Access Token(這種方式存在著一些安全問題)。除非沒得選,不然不會考慮這種方式,因為提出這種方式是十年前,瀏覽器功能受限,現如今功能更加強大,更多是採用授權碼型別加PKCE來代替隱式授權型別。
https://developer.okta.com/blog/2019/05/01/is-the-oauth-implicit-flow-dead
核心部分在於紅色部分,細講這幾部分
1、當前訪問的客戶端的URL地址重定向到授權伺服器的授權地址(2.0)
https://authorization-server.com/auth
?response_type=token
&client_id=29352915982374239857
&redirect_uri=https%3A%2F%2Fexample-app.com%2Fcallback
&scope=create+delete
&state=xcoiv98y2kd22vusuye3kch
引數說明:
response_type=token
必選,向授權伺服器標識客戶端使用隱式授權方式client_id
必選,應用的身份標識Id(應用先得在授權伺服器中登記得到一對ClientId和Client Secret)。redirect_uri
可選,授權成功後從授權伺服器重定向回client的地址。scope
可選,表示Client申請授權的範圍,用一個或多個空格分隔。state
推薦,Client提供的一個字串,伺服器會原樣返回給Client,以阻止CSRF攻擊。
2、授權頁中同意授權,重定向回Client提供的重定向地址(3.0~3.3)
https://example-app.com/redirect
#access_token=g0ZGZmNjVmOWIjNTk2NTk4ZTYyZGI3
&state=xcoiv98y2kd22vusuye3kch
&expires_in=3600
&token_type=Bearer
&scope=create
引數說明:
state
為Client提供的字串,授權伺服器原樣返回,用於Client防止CSRF攻擊。access_token
授權伺服器生成的訪問令牌(臨時的)。token_type
訪問令牌預設的schema。expires_in
訪問令牌的過期時間(秒數)。scope
Client申請授權的範圍。
問題解惑
1、這樣返回的Access token生命週期很短同時又不會返回Refresh token,如果多次請求,那麼就失效了得重新登入了?
答:隱式授予流程不能用於獲取重新整理令牌,由於基於瀏覽器的應用基本上都是短時的連線,僅持續載入它們的瀏覽器的上下文的會話長度,因此,重新整理令牌的用途非常有限。與其他授予型別不同,可以假定資源所有者仍處於瀏覽器中,並在必要的時候可用於重新授權客戶端。授權伺服器仍能夠應用首次使用信任(TOFU)原則從而使重新認證成為無縫的使用者體驗。
需要注意
重定向返回時的返回引數形式上和授權碼模式有所不一樣,隱式授權將token資訊放在了url的hash部分(#後面),而不是作為查詢字串(?後面)。這樣瀏覽器在訪問重定向的Location指定的url時,就不會把這些資料傳送到伺服器。而Client可以通過讀取Location頭資訊中獲取到access_token資訊。
客戶端憑證授權
有些場景下,資源所有者不能直接參與,即沒有與使用者的互動這一過程,像後臺任務,定時Job等是直接由Client Server去請求Authorization Server,這種即Machine to Machine。
核心部分在於紅色部分,細講這幾部分
1、當前客戶端發起Post請求授權伺服器(4.1)
POST請求
https://authorization-server.com/auth
Content-Type: application/x-www-form-urlencoded
grant_type=client_credentials
scope=create+delete
攜帶請求頭,其中Basic對應值xxx為clientid:clientsecret然後經base64編碼
Authorization: Basic xxx
客戶端提供以下引數請求Authorization Server:
- grant_type:必選。值固定為“client_credentials”。
- scope:可選。表示授權範圍。
2、授權伺服器認證Client端資訊無誤後返回訪問令牌(4.2)
引數說明:
access_token
授權伺服器生成的訪問令牌(臨時的)。token_type
訪問令牌預設的schema。expires_in
訪問令牌的過期時間(秒數)。scope
Client申請授權的範圍。
資源所有者密碼憑證授權
這種方式將資源所有者的身份標識資訊(賬號密碼,提供給Client,然後Client再去授權伺服器換取訪問令牌),這種安全風險大,更多是完全信任Client才會使用。
核心部分在於紅色部分,細講這幾部分
1、當前客戶端發起Post請求授權伺服器(4.1~4.2)
POST請求
https://authorization-server.com/auth
Content-Type: application/x-www-form-urlencoded
grant_type=password&username=yourname&password=123qwe
請求頭,其中Basic對應值xxx為clientid:clientsecret然後經base64編碼
Authorization: Basic xxx
- grant_type:必選。值固定為“password”。
- username:必選。使用者登陸名。
- passward:必選。使用者登陸密碼。
- scope:可選。表示授權範圍。
參考
https://developer.okta.com/
https://www.cnblogs.com/linianhui/p/oauth2-authorization.html#auto-id-4
2022-02-26,望技術有成後能回來看見自己的腳步