Spring Security 6.3.0-M3 中的令牌交換支援

工程 | Steve Riesenberg | 2024 年 3 月 19 日 | ...

我很高興地宣佈,Spring Security 6.3 將支援 OAuth 2.0 令牌交換授權型別 (RFC 8693),該支援已在最新的里程碑版本 (6.3.0-M3) 中提供預覽。此支援使能夠將令牌交換與 OAuth2 Client 一起使用。類似地,Spring Authorization Server 1.3 也隨之釋出了伺服器端支援,目前可在最新的里程碑版本 (1.3.0-M3) 中進行預覽。

Spring Security 的 OAuth2 Client 功能使我們能夠輕鬆地向使用 OAuth2 bearer 令牌保護的 API 發出受保護資源請求。類似地,Spring Security 的 OAuth2 Resource Server 功能使我們能夠使用 OAuth2 保護 API。接下來讓我們看看如何利用新支援來構建基於令牌交換的 OAuth2 流程。

示例

假設我們有一個名為 user-service 的資源伺服器,提供訪問使用者資訊 API。為了向 user-service 發出請求,客戶端必須提供訪問令牌。假設令牌必須具有 user-service 的 audience (aud claim)。這在 Spring Boot 配置屬性中可能如下所示

spring:
  security:
    oauth2:
      resourceserver:
        jwt:
          issuer-uri: https://my-auth-server.com
          audiences: user-service

現在假設我們想引入一個名為 message-service 的新資源伺服器,並從 user-service 中呼叫它。那麼假設這個新服務的令牌必須具有 message-service 的 audience。顯然,我們不能在對 message-service 的請求中重用對 user-service 請求中的令牌。但是,我們希望保留原始請求中使用者的身份。我們該如何實現這一點?

為了獲取 message-service 所需的訪問令牌,資源伺服器 user-service 必須成為一個客戶端,並用現有令牌交換一個保留原始令牌身份(使用者)的新令牌。這被稱為 “模擬 (impersonation)”,這正是 OAuth 2.0 令牌交換的設計場景。

將資源伺服器配置為客戶端

要啟用令牌交換,我們需要將 user-service 配置為既是資源伺服器,是可以使用令牌交換的客戶端,如下面的 Spring Boot 配置屬性所示

spring:
  security:
    oauth2:
      resourceserver:
        jwt:
          issuer-uri: https://my-auth-server.com
          audiences: user-service
      client:
        registration:
          my-token-exchange-client:
            provider: my-auth-server
            client-id: token-client
            client-secret: token
            authorization-grant-type: urn:ietf:params:oauth:grant-type:token-exchange
            client-authentication-method: client_secret_basic
            scope:
                - message.read
        provider:
          my-auth-server:
            issuer-uri: https://my-auth-server.com

我們還需要在 Spring Security 中啟用新的授權型別,這可以透過釋出以下 bean 來實現

    @Bean
    public OAuth2AuthorizedClientProvider tokenExchange() {
        return new TokenExchangeOAuth2AuthorizedClientProvider();
    }

這就是開始使用令牌交換所需的全部內容。但是,如果我們想請求特定的 audienceresource 值,則需要在令牌請求中配置額外的引數,如下例所示

    @Bean
    public OAuth2AuthorizedClientProvider tokenExchange() {
        var requestEntityConverter = new TokenExchangeGrantRequestEntityConverter();
        requestEntityConverter.addParametersConverter((grantRequest) -> {
            var parameters = new LinkedMultiValueMap<String, String>();
            parameters.add(OAuth2ParameterNames.AUDIENCE, "message-service");
            parameters.add(OAuth2ParameterNames.RESOURCE, "https://example.com/messages");

            return parameters;
        });

        var accessTokenResponseClient = new DefaultTokenExchangeTokenResponseClient();
        accessTokenResponseClient.setRequestEntityConverter(requestEntityConverter);

        var authorizedClientProvider = new TokenExchangeOAuth2AuthorizedClientProvider();
        authorizedClientProvider.setAccessTokenResponseClient(accessTokenResponseClient);

        return authorizedClientProvider;
    }

完成此配置後,我們可以在一個資源伺服器中獲取訪問令牌,並在向另一個資源伺服器傳送受保護資源請求時將其用作 Bearer 令牌。預設情況下,傳遞給資源伺服器的 Authorization 頭中的原始 bearer 令牌將用於獲取新的訪問令牌。

提示:有關如何使用此配置獲取訪問令牌併發出受保護資源請求的更多資訊,請參閱參考文件中的 授權客戶端功能

在伺服器端啟用令牌交換

為了完善整個流程,我們來構建一個全新的使用 Spring Authorization Server 的授權伺服器應用來支援此流程。

使用 Spring Initializr 並新增 OAuth2 Authorization Server 依賴,我們可以使用以下 Spring Boot 配置屬性配置一個功能齊全的授權伺服器

spring:
  security:
    user:
      name: sally
      password: password
    oauth2:
      authorizationserver:
        client:
          test-client:
            registration:
              client-id: test-client
              client-secret: {noop}secret
              client-authentication-methods:
                - client_secret_basic
              authorization-grant-types:
                - authorization_code
                - refresh_token
              scopes:
                - user.read
          token-client:
            registration:
              client-id: token-client
              client-secret: {noop}token
              client-authentication-methods:
                - client_secret_basic
              authorization-grant-types:
                - urn:ietf:params:oauth:grant-type:token-exchange
              scopes:
                - message.read

與客戶端類似,我們可能希望支援令牌交換的特定請求引數,例如 audienceresource,這可以透過釋出以下 bean 來實現

	@Bean
	public OAuth2TokenCustomizer<JwtEncodingContext> accessTokenCustomizer() {
		return (context) -> {
			if (AuthorizationGrantType.TOKEN_EXCHANGE.equals(context.getAuthorizationGrantType())) {
				OAuth2TokenExchangeAuthenticationToken tokenExchangeAuthentication = context.getAuthorizationGrant();
				Set<String> resources = tokenExchangeAuthentication.getResources();
				// TODO: Validate resource value(s) and map to the
				//  appropriate audience value(s) if needed...

				context.getClaims().audience(...);
			}
		};
	}

完成此配置後,授權伺服器將支援帶有 OAuth 2.0 令牌請求的可選 resource 引數的令牌交換授權型別,並能夠頒發允許資源伺服器作為客戶端模擬終端使用者的令牌。

結論

在這篇部落格文章中,我們討論了令牌交換的“模擬”用例,並探討了資源伺服器(作為客戶端)和授權伺服器的簡單配置。

提示:有關其他示例,包括也受支援的名為“委託 (delegation)”的額外用例示例,請參閱 RFC 8693附錄 A

我希望您和我一樣對這項新支援感到興奮!我鼓勵您嘗試 Spring Authorization Server 中的 示例,其中包含這篇部落格文章的一個工作示例。也請在您自己的專案中嘗試 Spring Security 和 Spring Authorization Server 的里程碑版本。我們非常樂意收到您的反饋!

訂閱 Spring 資訊

透過 Spring 資訊保持聯絡

訂閱

領先一步

VMware 提供培訓和認證,助您快速提升。

瞭解更多

獲取支援

Tanzu Spring 透過一個簡單的訂閱提供 OpenJDK™、Spring 和 Apache Tomcat® 的支援和二進位制檔案。

瞭解更多

近期活動

檢視 Spring 社群的所有近期活動。

檢視全部