OAuth2(二)——实现
目錄
OAuth客戶端
向授權(quán)服務(wù)器注冊 OAuth 客戶端
使用授權(quán)碼許可類型獲取令牌
發(fā)送授權(quán)請求
處理授權(quán)響應(yīng)
使用 state 參數(shù)添加跨站保護(hù)
使用令牌訪問受保護(hù)資源
刷新訪問令牌
OAuth 受保護(hù)資源
解析 HTTP 請求中的 OAuth 令牌
根據(jù)數(shù)據(jù)存儲(chǔ)驗(yàn)證令牌
根據(jù)令牌提供內(nèi)容
不同的權(quán)限范圍對應(yīng)不同的操作
不同的權(quán)限范圍對應(yīng)不同的數(shù)據(jù)結(jié)果
不同的用戶對應(yīng)不同的數(shù)據(jù)結(jié)果
額外的訪問控制
OAuth 授權(quán) 服務(wù)器
管理 OAuth 客戶端注冊
對客戶端授權(quán)
授權(quán)端點(diǎn)
客戶端授權(quán)
令牌頒發(fā)
對客戶端進(jìn)行身份認(rèn)證
處理授權(quán)許可請求
支持刷新令牌
增加授權(quán)范圍的支持
OAuth2的4種授權(quán)模式
隱式授權(quán)模式/簡化模式(implicit)
優(yōu)點(diǎn):
缺點(diǎn):
適用場景
密碼模式(resource owner password credentials)
優(yōu)點(diǎn):
缺點(diǎn):
應(yīng)用場景:
客戶端憑據(jù)模式(client credentials)
OAuth客戶端
向授權(quán)服務(wù)器注冊 OAuth 客戶端
OAuth 客戶端(一般是一個(gè)應(yīng)用)由一個(gè)稱為“客戶端標(biāo)識(shí)符”的特殊字符串來標(biāo)識(shí)(一般:client_id),每個(gè)客戶端的標(biāo)識(shí)符必須唯一,因此,客戶端標(biāo)識(shí)符幾乎總是由授權(quán)服務(wù)器來分配。還需要一個(gè)客戶端憑據(jù)(一般用client_secret標(biāo)識(shí)),用于與授權(quán)服務(wù)器交互時(shí)對自身進(jìn)行身份認(rèn)證。
客戶端 需要知道授權(quán)端點(diǎn)和令牌端點(diǎn)的位置,除此之外不需要知道有關(guān)服務(wù)器的任何其他信息。
使用授權(quán)碼許可類型獲取令牌
OAuth 客戶端要從授權(quán)服務(wù)器獲取令牌,需要資源擁有者以某種形式授權(quán)。我們 將使用一種被稱為授權(quán)碼許可類型的交互式授權(quán)形式,由客戶端將資源擁有者重定向至授權(quán)服務(wù)器的授權(quán)端點(diǎn)。然后,服務(wù)器通過 redirect_uri 將授權(quán)碼返回 給客戶端。最后,客戶端將收到的授權(quán)碼發(fā)送到授權(quán)服務(wù)器的令牌端點(diǎn),換取 OAuth 訪問令牌, 再進(jìn)行解析和存儲(chǔ)。
發(fā)送授權(quán)請求
為了啟動(dòng)授權(quán)流程,需要將用戶重定向至授權(quán)服務(wù)器的授權(quán)端點(diǎn),并在授權(quán)端點(diǎn)的 URL 中 包含所有適當(dāng)?shù)牟樵儏?shù)。
處理授權(quán)響應(yīng)
并從 code 參數(shù)中讀取授權(quán)服務(wù)器返回 的授權(quán)碼。請記住,授權(quán)服務(wù)器通過重定向讓瀏覽器向客戶端發(fā)起請求,而不是直接響應(yīng)客戶端 請求。拿到這個(gè)授權(quán)碼,并使用 HTTP POST 方法將其直接發(fā)送至令牌端點(diǎn)。
為什么在這個(gè)請求中包含 redirect_uri?畢竟此處是不需要執(zhí)行重定向的。根據(jù) OAuth 規(guī)范,如果在授權(quán)請求中指定了重定向 URI,那么令牌請求中也必須包含該重定向 URI。 這可以防止攻擊者使用被篡改的重定向 URI 獲取受害用戶的授權(quán)碼,讓并無惡意的客戶端將受 害用戶的資源訪問權(quán)限關(guān)聯(lián)到攻擊者賬戶。
還需要添加一些請求頭來標(biāo)識(shí)這是一個(gè) HTTP 表單格式的請求,并使用 HTTP 基本認(rèn)證對客 戶端進(jìn)行身份認(rèn)證。在 HTTP 基本認(rèn)證中,Authorization 頭部是一個(gè) Base64 編碼的字符串, 編碼的內(nèi)容是拼接后的用戶名和密碼,以冒號分隔。OAuth 2.0 要求將客戶端 ID 作為用戶名,將 客戶端密鑰作為密碼,但使用之前應(yīng)該先對它們分別進(jìn)行 URL 編碼。
使用 state 參數(shù)添加跨站保護(hù)
客戶端接受收到的 code 值,會(huì)試圖將其發(fā)送給授權(quán)服務(wù)器。這意味著攻擊者可能會(huì)用客戶端向授權(quán)服務(wù)器暴力搜索有效的授權(quán)碼,浪費(fèi)客戶端和授權(quán)服務(wù)器資源,而且還有可能導(dǎo)致客戶端獲取一個(gè)從未請求過的令牌。
可以使用一個(gè)名為 state 的可選 OAuth 參數(shù)來緩解這個(gè)問題,將該參數(shù)設(shè)置為一個(gè)隨機(jī)值,并在應(yīng)用中用一個(gè)變量保存它。在丟棄舊的訪問令牌之后,我們會(huì)創(chuàng)建一個(gè) state 值。
?
使用令牌訪問受保護(hù)資源
現(xiàn)在已經(jīng)有了一個(gè)訪問令牌,戶端要做的就是使用令牌向受保護(hù)資源發(fā)出調(diào)用請求,有 3 個(gè)合法的位置可以用于攜帶
令牌。在客戶端中,使用 HTTP Authorization 頭部來傳遞令牌,這是規(guī)范推薦盡可能使用的 方法。我們得到的這種訪問令牌叫作 bearer 令牌,它意味著無論是誰,只要持有該令牌就可以向 受保護(hù)資源出示。OAuth bearer 令牌使用規(guī)范明確給出了發(fā)送令牌值的 3 種方法:
? 使用 HTTP Authorization 頭部;
? 使用表單格式的請求體參數(shù);
? 使用 URL 編碼的查詢參數(shù)。
使用 Authorization 頭部是這 3種方法中最靈活和最安全的,但是對于某些客戶端來說, 使用起來很困難。一個(gè)健壯的 OAuth 客戶端或服務(wù)端庫應(yīng)該完整地提供這 3 種方式,以適應(yīng) 不同情況。
刷新訪問令牌
現(xiàn)在已經(jīng)可以使用訪問令牌訪問受保護(hù)資源了,但是如果訪問令牌過期了怎么辦呢?還要再 次勞煩用戶為客戶端應(yīng)用授權(quán)嗎?
OAuth 2.0 提供了一種在無須用戶參與的情況下獲取新訪問令牌的方法:刷新令牌。這是一 項(xiàng)很重要的功能,因?yàn)橛脩粼诔醮问跈?quán)完成之 后不會(huì)一直在場,而 OAuth 經(jīng)常要在這樣的情況下使用OAuth 受保護(hù)資源
解析 HTTP 請求中的 OAuth 令牌
解析 HTTP 請求中的 OAuth 令牌。首先,嘗試從請求中獲取 Authorization 頭部(如果請求中包含),然后檢查它是否包含 OAuth bearer 令牌,將頭部中的 bearer 關(guān)鍵字與后跟的空格去掉,獲取令牌值。接下來,要處理通過表單參數(shù)傳遞的令牌,表單參數(shù)在請求主體中。最后一種方法是通過查詢參數(shù)傳遞令牌。
?
OAuth bearer 令牌使用規(guī)范規(guī)定,在使用 HTTP Authorization 頭部傳遞令牌時(shí),HTTP 頭的值以關(guān)鍵字 Bearer 開頭,后跟一個(gè)空格,再跟令牌值本身。而且,OAuth 規(guī)范還規(guī)定 Bearer 關(guān)鍵字不區(qū)分大小寫。此外,HTTP 規(guī)范還規(guī)定了 Authorization 頭部關(guān)鍵字本身不區(qū)分大小 寫。
根據(jù)數(shù)據(jù)存儲(chǔ)驗(yàn)證令牌
根據(jù)令牌提供內(nèi)容
很多 API 設(shè)計(jì)中, 不同的操作需要不同的訪問權(quán)限。還有一些 API 會(huì)根據(jù)授權(quán)者不同而返回不同的結(jié)果,或者根據(jù) 不同權(quán)限返回某一部分信息。
不同的權(quán)限范圍對應(yīng)不同的操作
不同的權(quán)限范圍對應(yīng)不同的數(shù)據(jù)結(jié)果
不同的用戶對應(yīng)不同的數(shù)據(jù)結(jié)果
額外的訪問控制
OAuth 授權(quán) 服務(wù)器
管理 OAuth 客戶端注冊
OAuth 服務(wù)器需要為每一個(gè)客戶端分配唯一的客戶端標(biāo)識(shí)符。對客戶端授權(quán)
OAuth 協(xié)議要求授權(quán)服務(wù)器提供兩個(gè)端點(diǎn):授權(quán)端點(diǎn),運(yùn)行在前端信道上;令牌端點(diǎn),運(yùn)行 在后端信道上。授權(quán)端點(diǎn)
授權(quán)端點(diǎn)是一個(gè)前端信道端點(diǎn),客戶端會(huì) 將用戶瀏覽器重定向至該端點(diǎn),以發(fā)出授權(quán)請求。
首先,需要確定發(fā)出請求的是哪一個(gè)客戶端。客戶端會(huì)在請求中以 client_id 傳遞其標(biāo)識(shí)符。
接下來,需要檢查客戶端是否存在。
最后,如果客戶端通過檢查,則需要渲染出一個(gè)頁面來請求用戶授權(quán)。用戶需要與這個(gè)頁面 交互,并向授權(quán)服務(wù)器提交授權(quán)決策,這將需要瀏覽器向授權(quán)服務(wù)器再發(fā)送一個(gè) HTTP 請求。
客戶端授權(quán)
令牌頒發(fā)
生成的授權(quán)碼通過客戶端的重定向 URI 返回來了。客戶端拿到授權(quán)碼 之后,向授權(quán)服務(wù)器的令牌端點(diǎn)發(fā)出一個(gè) POST 請求。這屬于后端信道通信,是在客戶端和授權(quán) 服務(wù)器之間進(jìn)行的,不需要用戶瀏覽器參與。
對客戶端進(jìn)行身份認(rèn)證
處理授權(quán)許可請求
首先,應(yīng)該檢查 grant_type 參數(shù),以確保收到的許可類型是我們所支持的。驗(yàn)證授權(quán)碼是否有效(需要驗(yàn)證是否頒發(fā)給此客戶端),如果有效生成令牌(需要存儲(chǔ))。
支持刷新令牌
增加授權(quán)范圍的支持
通過scope(以空格分隔的字符串)返回權(quán)限范圍。
?
OAuth2的4種授權(quán)模式
授權(quán)碼模式(authorization code)
授權(quán)碼模式(authorization code)是功能最完整、流程最嚴(yán)密的授權(quán)模式,code保證了token的安全性,即使code被攔截,由于沒有app_secret,也是無法通過code獲得token的。
?
?
1、用戶訪問客戶端(一個(gè)應(yīng)用),客戶端通過用戶代理(一般指瀏覽器)向認(rèn)證服務(wù)器請求授權(quán)碼;
客戶端:http://localhost:8882/? 訪問 受限資源 被重定向
Request URL: http://localhost:8882/securedPage Request Method: GET Status Code: 302 Remote Address: [::1]:8882 Referrer Policy: no-referrer-when-downgrade定向到本服務(wù)login:
Location: http://localhost:8882/login Pragma: no-cache Set-Cookie: JSESSIONID=4E3CFEF7BABEBDE1ACF8F66AF70291ED; Path=/; HttpOnly X-Content-Type-Options: nosniff X-Frame-Options: DENY X-XSS-Protection: 1; mode=block?本服務(wù)login又被重定向到授權(quán)端點(diǎn)。
Request URL: http://localhost:8882/login Request Method: GET Status Code: 302 Remote Address: [::1]:8882 Referrer Policy: no-referrer-when-downgrade Location: http://localhost:8881/auth/oauth/authorize?client_id=cloud&redirect_uri=http://localhost:8882/login&response_type=code&state=PGNoFC Pragma: no-cache X-Content-Type-Options: nosniff X-Frame-Options: DENY X-XSS-Protection: 1; mode=block?
授權(quán)端點(diǎn)會(huì)重定向到授權(quán)服務(wù)的login
Request URL: http://localhost:8881/auth/oauth/authorize?client_id=cloud&redirect_uri=http://localhost:8882/login&response_type=code&state=PGNoFC Request Method: GET Status Code: 302 Found Remote Address: [::1]:8881 Referrer Policy: no-referrer-when-downgrade Location: http://localhost:8881/auth/login Pragma: no-cache Set-Cookie: JSESSIONID=n1kUeYp97i90b7D3dtlDn7PevVnyEZo8Hhxqi3tN; path=/auth X-Content-Type-Options: nosniff X-Frame-Options: DENY X-XSS-Protection: 1; mode=block??在此步會(huì)產(chǎn)生授權(quán)服務(wù)器的SESSIONID(JSESSIONID=n1kUeYp97i90b7D3dtlDn7PevVnyEZo8Hhxqi3tN)
攜帶參數(shù):
lient_id: cloud redirect_uri: http://localhost:8882/login response_type: code state: PGNoFC2、用戶同意授權(quán)(資源所有者操作);
Cookie: JSESSIONID=n1kUeYp97i90b7D3dtlDn7PevVnyEZo8Hhxqi3tN; JSESSIONID=4E3CFEF7BABEBDE1ACF8F66AF70291ED Host: localhost:8881 Referer: http://localhost:8882/?執(zhí)行一個(gè)POST LOGIN請求,又重定向到授權(quán)端點(diǎn)。
Request URL: http://localhost:8881/auth/login Request Method: POST Status Code: 302 Found Remote Address: [::1]:8881 Referrer Policy: no-referrer-when-downgrade?
Location: http://localhost:8881/auth/oauth/authorize?client_id=cloud&redirect_uri=http://localhost:8882/login&response_type=code&state=PGNoFC Pragma: no-cache Set-Cookie: JSESSIONID=rAkHws2uu-SFx_eFmnGysNrC4h2jotWlemLnAaS8; path=/auth X-Content-Type-Options: nosniff?cookie 重新設(shè)置。
?
3、認(rèn)證服務(wù)器通過用戶代理返回授權(quán)碼給客戶端;
equest URL: http://localhost:8881/auth/oauth/authorize?client_id=cloud&redirect_uri=http://localhost:8882/login&response_type=code&state=PGNoFC Request Method: GET Status Code: 303 See Other Remote Address: [::1]:8881 Referrer Policy: no-referrer-when-downgrade?攜帶授權(quán)碼返回客戶端
Request URL: http://localhost:8882/login?code=tNQKUP&state=PGNoFC Request Method: GET Status Code: 302 Remote Address: [::1]:8882 Referrer Policy: no-referrer-when-downgrade4、客戶端攜帶授權(quán)碼向認(rèn)證服務(wù)器請求訪問令牌(AccessToken);
5、認(rèn)證服務(wù)器返回訪問令牌;
6、客戶端攜帶訪問令牌向資源服務(wù)器請求資源;
7、資源服務(wù)器返回資源。
?
隱式授權(quán)模式/簡化模式(implicit)
implicit模式(隱式模式)和授權(quán)碼模式(authorization_code)訪問差不多,相比之下,少了一步獲取code的步驟,而是直接獲取token。
授權(quán)碼許可流程中各個(gè)步驟的關(guān)鍵是不同組件之間保持信息隔離。通過這種方式,瀏覽器接觸不到只應(yīng)由客戶端掌握的信息,客戶端也無法得知瀏覽器的狀態(tài)。但是如果把客戶端放在瀏覽器內(nèi)部運(yùn)行會(huì)怎么樣呢?
客戶端無法對瀏覽器隱藏任何秘密,因?yàn)闉g覽器對客戶端的任何動(dòng)作都了如指掌。在這種情況下,通過瀏覽器向客戶端傳遞僅用于換
取令牌的授權(quán)碼就沒有任何實(shí)際意義了,因?yàn)檫@個(gè)額外的保密層沒有起到任何作用。
隱式許可類型沒有使用這個(gè)額外的保密層,而是直接從授權(quán)端點(diǎn)返回令牌。因此隱式許可類型只使用前端信道和授權(quán)服務(wù)器通信。這種授權(quán)許可流程對內(nèi)嵌在網(wǎng)站上的JavaScript 應(yīng)用非常有用,這些應(yīng)用需要在不同安全域中進(jìn)行經(jīng)過授權(quán)的、可能受限的會(huì)話共享。
客戶端向授權(quán)服務(wù)器的授權(quán)端點(diǎn)發(fā)送請求時(shí),使用的方式與授權(quán)碼流程相同,只不過 response_type 參數(shù)的值為 token,而不是 code。這樣會(huì)通知授權(quán)服務(wù)器直接生成令牌,而 不是生成一個(gè)用于換取令牌的授權(quán)碼。
?
優(yōu)點(diǎn):
- 簡單
缺點(diǎn):
- 不能存儲(chǔ)獲取refresh_token的必要新,不能獲取refresh_token
- token暴露風(fēng)險(xiǎn)
適用場景
應(yīng)用只有頁面,沒有后臺(tái)管理,只能適用第三方驗(yàn)證。例如問卷調(diào)查,評論。
為web瀏覽器應(yīng)用設(shè)計(jì)
?
密碼模式(resource owner password credentials)
也是拿憑據(jù)(授權(quán)服務(wù)的憑據(jù))獲取token,與隱式授權(quán)模式的區(qū)別,登錄界面是客戶端提供的,客戶端會(huì)持有憑據(jù)。
支持refresh token。
優(yōu)點(diǎn):
- 不需要多次請求轉(zhuǎn)發(fā),額外開銷,同時(shí)可以獲取更多的用戶信息。(拿到賬號密碼了)
缺點(diǎn):
- 局限性,認(rèn)證服務(wù)器和應(yīng)用方必須有超高的信賴。
應(yīng)用場景:
-
自家公司搭建的認(rèn)證服務(wù)器
-
遺留項(xiàng)目升級為oauth2的適配方案
客戶端憑據(jù)模式(client credentials)
如果沒有明確的資源擁有者,或?qū)τ诳蛻舳塑浖碚f資源擁有者不可區(qū)分,該怎么辦?這是 一種相當(dāng)常見的場景,比如后端系統(tǒng)之間需要直接通信,但是它們并不一定代表某個(gè)特定用戶。
這是一種最簡單的模式,只要client請求,我們就將AccessToken發(fā)送給它。這種模式是最方便但最不安全的模式。因此這就要求我們對client完全的信任,而client本身也是安全的。不支持refresh token,主要是沒有必要。
因此這種模式一般用來提供給我們完全信任的服務(wù)器端服務(wù)。在這個(gè)過程中不需要用戶的參與。
示例:?https://gitee.com/demon75520/spring-security-oauth-demo#spring-security-oauth-demo
總結(jié)
以上是生活随笔為你收集整理的OAuth2(二)——实现的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Gitlab运维
- 下一篇: Spring Security OAut