OAuth2 and Friends Resource Server

導讀

讀了此篇文章,你應該能知道 Spring Cloud OAuth2 的 Resource Server 應該怎麼配置以及爲什麼這麼配置了。

簡述

OAuth2 協議中 Resource Server 是要保護的對象,也就是說當 Resource Server 上的某個資源被請求時,Resource Server 要有能力去驗證 Access Token 擁有需要的權限(scope),僅此而已。

要查看 Access Token 持有的權限,有兩種辦法,一種是利用加密算法(對稱加密和非對稱加密均可),Authorization Server 使用Private Key 加密 Access Token,在 Resource Server 使用 Public Key 解密 Access Token,即可查看到其中的權限(Scope)。

第二種方法是使用 OAuth2 中規定 Authorization Server 提供的兩個 Endpoint,Check Token 和 UserInfo Endpoint。

Spring Cloud OAuth2 中對上述方法兩種方式提供了全面的支持。下面進行詳細的講解。

Spring Cloud OAuth2 Resource Server

如果你想從源碼級別瞭解 Spring Cloud OAuth2 對上述兩種方式的支持,可以直接查看 ResourceServerTokenServices 接口及其實現。在 Spring Cloud OAuth2 中 ,ResourceServerTokenServices 接口有 DefaultTokenServices、RemoteTokenServices 和 UserInfoTokenServices 三個實現。

DefaultTokenServices 對應非對稱加密的方式,其持有一個 JwkVerifyingJwtAccessTokenConverter 對象,用於將 Access Token 解密爲明文。

RemoteTokenServices 對應 Check Token 的方式,調用配置的 Check Token Endpoint URI,解析返回的信息。

UserInfoTokenServices 對應 UserInfo 的方式,調用配置的 UserInfo Endpoint URI,解析返回的信息。

RemoteTokenCondition提供了TokenInfo,UserInfo兩種方式
JwtTokenCondition
JwKCondition
JwtKeyStoreCondition

利用加密算法

Jwk

這種方式只需要配置讓 Spring Security OAuth2 知道用來解密的 Public Key 即可。

security: 
  oauth2:
    resource:
      jwk:
        key-set-uri: http://localhost:8080/uaa/token_keys

如上這是最簡單的 Resource Server 的配置方式,Spring Security OAuth2 通過 key-set-uri 配置的 URI 地址提供的 Public Key ,構建 JwkVerifyingJwtAccessTokenConverter 對象,用於將 Access Token 解密爲明文 JWT Claims。

這種使用方式是最簡單的,同時也是 JWT 最開始的初衷,Authorization Server 發放 JWT ,但是鑑權過程由 Resource Server 自己完成,大大減輕了 Authorization Server 的壓力。這種方式的唯一缺陷就是無法驗證 JWT 是否被 Revoke。

Jwt

通上邊的方式是一個原理,只不過配置公鑰的方式有變化。可以使用如下的方式配置一個URL地址,其返回的 JSON 只要含有 value 字段,值爲對應的公鑰即可。

當然也可以使用如下的方式直接配置公鑰的值。

Jwt KeyStore

如果你的密鑰對存儲到了一個 keystore 中,也可以將此 keystore 直接配置給Spring Cloud OAuth2。由於 keystroe 是有用戶名和密碼的,keystore 也就是安全的,雖然此時的 keystore 中同時存儲了公鑰和私鑰,也是沒有問題的。

利用Check Token Endpoint

如果我們需要一個 JWT 可以被回收,由於 JWT 自身的完整性,那麼唯一的方法就是每次想查看 Access Token 持有的信息的時候,不是直接解密 Access Token 。而是使用 Authorization Server 提供的Check Token 和 UserInfo Endpoint。
使用 Check Token Endpoint

security: 
  oauth2:
    resource:
      token-info-uri: http://localhost:8080/uaa/check_token
      prefer-token-info: true
    client:
      client-id: app
      client-secret: appclientsecret

注意:如果你也在使用CloudFoundry的 UAA服務,那麼這裏的 Check Token Endpoint 需要使用 Introspect Token Endpoint, 原因是 Introspect Token Endpoint 返回的信息裏比 Check Token 多一個 active 字段,這個字段標識當前用戶是否可用。在 Spring Security OAuth2 的 RemoteTokenServices 類中可以看到 active 信息的判斷,不管是 Check Token Endpoint 還是 Introspect Token Endpoint ,只要 active 不是 true,則拋出 InvalidTokenException 異常。

注意:這裏需要配置 client-id 和 client-secret 的原因是 Check Token Endpoint, Introspect Token Endpoint 和 UserInfo Endpoint,這三個 Endpoint 在請求時都是需要一定的權限的。比如在 CloudFoundry 的 UAA 中,Check Token Endpoint 和 Introspect Token Endpoint 就需要 uaa.resource 權限。

利用UserInfo Endpoint

OAuth2 協議中規定 Authorization Server 中需要提供 UserInfo Endpoint 用於客戶端查看用戶信息。

security: 
  oauth2:
    resource:
      user-info-uri: http://localhost:8080/uaa/userinfo

配置上也很簡單,由於 UserInfo 接口的驗證方式是 Bear Token ,也就是只需要傳入對應的 Access Token 即可,所以不需要配置 client_id 和 client_secret 。

如果你的 Authorization Server 使用的是 CloudFoundry 的 UAA ,那麼最好不要使用這個接口, 具體可以查看 UAA 中的 UserInfoEndpoint 的源碼。簡單說的話,有兩點,就權限來說,在 CloudFoundry 的 UAA 中,前兩種方式獲取到的權限是來自 UAA 的 Group 模塊;而當前方式獲取到的權限信息來自 UAA 中的 User 模塊,在具體點是來自user_info表的info字段。第二點是 Spring Cloud OAuth2 對此接口也不兼容,在 UAA 的 UserInfoEndpoint 中可以看到權限會被放在返回 JSON 的 roles 字段中,而從 Spring Cloud OAuth2 的 UserInfoTokenServices 持有的 FixedAuthoritiesExtractor 中可以看到, Spring Cloud OAuth2 是假設將信息存儲在了 authorities 、authority、role 或者 value 字段中。

後記

關於 Resource Server 的配置, Spring Cloud OAuth2 提供了以上五種配置方式;分爲兩大類,本地解密和遠程調用解析。

如果使用了第三方的 Authorization Server 注意各個 Endpoint 的權限驗證方式,比如 CloudFoundry 的 UAA 中,key set url 可以配置爲無需任何權限即可訪問, 也可以配置讓客戶端傳遞 client_id 和 client_secret 。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章