Oauth2協議那些事
阿新 • • 發佈:2021-04-01
## 1. 背景
首先,設想一種情境:你平常會使用一款`照片儲存App`(以下`照片服務`指代),用來將自己喜歡的照片存放在上面以備隨時檢視。假如有一天,你想要列印其中的某張照片而且你找到了一款`列印照片App`(以下`列印服務`指代)。此時你應該怎麼將照片給到列印App呢?其實這就涉及到了授權的問題,你必須讓`照片服務`授權`列印服務`訪問指定的照片。
也許,最直接的方式就是把使用者和密碼給到`列印服務`,這樣的話`列印服務`就可以代替你直接去登入`照片服務`拿到任意的照片。但是這種方式很明顯有極大的安全風險,因為你不能保證`列印服務`不會訪問你的其他照片並且去做壞事。因此在這種背景下,OAuth2協議應運而生。本文就圍繞Oauth2為主題,解析它的具體定義以及各種模式,更重要的是不同模式的使用場景。
## 2. Oauth2的相關概念
在具體講解Oauth2的定義之前,有幾個概念有必要先了解一下:
- **資源伺服器(Resource Server)**: 存放受保護資源的伺服器,`照片儲存App`在上面的情境中就可以看做是資源伺服器
- **資源擁有者(Resource Owner)**:**:通常就是指對資源有擁有權的使用者
- **客戶端(Client)**: 想要訪問受保護資源的物件,上面`列印照片App`就是一種客戶端(單頁應用、無線應用等也屬於客戶端)
- **授權伺服器(Authorization Server)**:給客戶端頒發授權憑證的伺服器,通常是給客戶端授權碼、令牌等憑證
## 3. Oauth2的授權模式
本節具體講解Oauth2四種不同的授權模式,分別是:`授權碼模式`、`隱含模式`、`密碼模式`、`客戶端憑證模式`,以及它們不同的使用場景。
### 3.1 授權碼模式(Authorization Code)
- **使用場景**:`授權碼模式`是Oauth2協議裡最安全的一種模式(微信、微博第三方登入都是基於該模式),如果上面`照片服務`和`列印服務`隸屬於不同公司,並且`列印服務`有自己的伺服器。那麼在這種情景下,必須要用`授權碼模式`保證資源的安全,它的主要流程是:
1. 客戶端需要先向授權伺服器請求授權碼`code`,請求的格式如下:
```
http://localhost:8080/oauth/authorize?
client_id=testClient&redirect_uri=http://localhost:8080/callback&response_type=code&scope=read_userInfo&state=x1f2xs
```
- **client_id**:客戶端憑證id,一般在授權伺服器提前註冊獲取
- **redirect_uri**: 回撥地址,用來回調授權碼給客戶端伺服器
- **response_type**: 用來告訴授權伺服器響應授權碼
- **scope**: 需要申請訪問的資源範圍
- **state**: 隨機數,用來防止csrf攻擊
2. 客戶端拿到授權碼之後,去向授權伺服器請求以獲取到訪問令牌`token`,請求格式如下:
```
curl -X POST --user testClient:testSecret http://localhost:8080/oauth/token
-H 'content-type:application/x-www-form-urlencoded'
-d "code=QvjUaf&grant_type=authorization_code&redirect_uri=http://localhost:8080/callback&scope=read_userInfo"
```
- **testClient:testSecret**:表名客戶端身份,客戶端id和客戶端祕鑰
- **code**: 授權碼
- **grant_type=authorization_code**: 表名授權方式是授權碼模式
- **redirect_uri**:回撥地址,一般後端伺服器會去訪問資源然後定位到首頁或者使用者頁
- **scope**:請求資源範圍
3. 拿到`token`後,客戶端就可以請求指定的api進行資源訪問,請求格式如下:
```
curl -X GET http://localhost:8080/api/xxx -H 'Authorization:Bearer LIYcLu400YNX5CzJpZ39XG7J9kw'
```
> 授權碼模式主要涉及到`code`和`token`的獲取,只有獲取到token後才有許可權訪問資源。`code`是通過前端的方式傳遞的,但是就算`code`被截獲,由於token是在伺服器中完成請求的,一般`clientSecret`是不會洩露的,並且獲取token還需`state`驗證(`state`會和會話進行繫結),因此token洩露的風險極小,所以推薦使用這種模式來頒發token。
### 3.2 隱含模式(implicit)
- **使用場景**:由於並不是所有的客戶端都有伺服器支援,假如`列印服務`是個`spa應用`,那麼`授權碼模式`很顯然並不適用,因此Oauth2還提供了`隱含模式`來處理這種情況。它的主要流程如下:
1. 客戶端向授權伺服器直接請求獲取`token`,請求格式如下:
```
http://localhost:8080/oauth/authorize
?client_id=testClient&redirect_uri=http://localhost:8080/callback&response_type=token&scope=read_userInfo
```
- **client_id**:客戶端憑證id,一般在授權伺服器提前註冊獲取
- **redirect_uri**: 回撥地址,用來將token回撥給客戶端
- **response_type**: 用來告訴授權伺服器響應token
- **scope**: 需要申請訪問的資源範圍
> 需要注意的是這種模式並不是一種十分安全的模式,因為token有被洩漏的風險。因此除非是像上述這種必要的場景,一般不適用這種模式,並且在這種模式下token的失效時間需要儘可能的短。
2. 拿到`token`後請求資源伺服器
```
curl -X GET http://localhost:8080/api/xxx -H 'Authorization:Bearer LIYcLu400YNX5CzJpZ39XG7J9kw'
```
### 3.3 密碼模式(password)
- **使用場景**:如果`照片服務和`列印服務`同屬一個公司,那麼可能不需要很嚴格的安全防範。因此Oauth2還提供了`密碼模式`來頒發`token`,主要流程如下:
1. 客戶端直接向授權伺服器請求獲取`token`介面,請求格式如下:
```
curl -X POST --user testClient:testSecret http://localhost:8080/oauth/token
-H 'content-type:application/x-www-form-urlencoded'
-d 'grant_type=password&username=joe&password=123&scope=read_userInfo'
```
- **testClient:testSecret**:表名客戶端身份,客戶端id和客戶端祕鑰
- **grant_type=password**: 表名授權方式是密碼模式
- **username=joe&password=123**:將密碼和使用者名稱傳遞給授權碼伺服器
- **scope**:請求資源範圍
> 這種模式嚴格來說是很不安全的,因為你把使用者名稱和密碼給了客戶端,然後代替你去登入網站。預設也不推薦使用這種模式,除非是特別信任的系統或者是同一組織的產品。
2. 拿到`token`後請求資源伺服器
```
curl -X GET http://localhost:8080/api/user/info -H 'Authorization:Bearer LIYcLu400YNX5CzJpZ39XG7J9kw'
```
### 3.4 客戶端憑證模式(Client Credentials)
- **使用場景**:單純機器和機器之間的互動,和使用者並沒有互動關係。那麼客戶端可能只需要向授權伺服器註冊好憑證儲存下來就可以進行認證。針對這種情況,Oauth2通過`客戶端憑證`的方式來頒發`token`,主要流程如下:
1. 獲取`token`
```
curl -X POST --user testClient:testSecret http://localhost:8080/oauth/token
-H 'content-type:application/x-www-form-urlencoded'
-d 'grant_type=client_credentials&scope=read_userInfo'
```
- **testClient:testSecret**:表名客戶端憑證,客戶端id和客戶端祕鑰
- **grant_type=client_credentials**: 表名授權方式是客戶端憑證模式
- **scope**:請求資源範圍
> 這種方式更加地簡單,只需要向授權伺服器申請過憑證,就可以訪問資源,一般用於純及機器之間的互動。
2. 拿到`token`後請求資源伺服器
```
curl -X GET http://localhost:8080/api/user/info -H 'Authorization:Bearer LIYcLu400YNX5CzJpZ39XG7J9kw'
```
## 4. 總結
至此,關於oauth2協議的四種模式已經講解完了,並且針對它們不同的使用場景也做了闡述。Oauth2協議在微服務系統盛行的今天有著很重要的作用,往往會在閘道器層擔當許可權控制的角色。並且Oauth2只是定義了協議,並不是具體的實現,像`Spring Security Oauth2`、`Google OAuth Java Client`等都對它有比較好的支援,具體的使用可以藉助這些框架來展開。
## 5. 參考資料
- https://oauth.net/code/java/
- Oauth2 in action
- https://www.ruanyifeng.com/blog/2014/05/oauth_2_0.html
> 轉載請註明出處 https://www.cnblogs.com/joebig7/p/14607