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 用戶端 一起使用的能力。同樣地,伺服器端支援也隨著 Spring Authorization Server 1.3 版本一同發布,並且現已在最新的里程碑版本 (1.3.0-M3) 中提供預覽。

Spring Security 的 OAuth2 用戶端功能讓我們能夠輕鬆地向使用 OAuth2 持有者令牌保護的 API 發出受保護資源請求。同樣地,Spring Security 的 OAuth2 資源伺服器功能讓我們能夠使用 OAuth2 保護 API。讓我們看看如何使用新的支援來建構具有令牌交換的 OAuth2 流程。

範例

讓我們想像一下,我們有一個名為 user-service 的資源伺服器,提供用於存取使用者資訊的 API。為了向 user-service 發出請求,用戶端必須提供訪問令牌。假設令牌必須具有 user-service 的受眾 (aud 聲明)。這可能看起來像以下的 Spring Boot 配置屬性

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

現在讓我們想像一下,我們想要引入一個名為 message-service 的新資源伺服器,並從 user-service 呼叫它。然後假設此新服務的令牌必須具有 message-service 的受眾。顯然,我們無法在對 message-service 的請求中重複使用來自對 user-service 的請求的令牌。但是,我們希望保留原始請求中用戶的身分。我們該如何完成此操作?

為了獲得 message-service 所需的訪問令牌,資源伺服器 user-service 必須成為用戶端並交換現有的令牌,以獲得保留原始令牌身分 (使用者) 的新令牌。這稱為 「模擬」,並且正是 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 標頭中的原始持有者令牌將用於取得新的訪問令牌。

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

在伺服器上啟用令牌交換

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

使用具有 OAuth2 Authorization Server 依賴項的 Spring Initializr,我們可以使用以下 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 參數的令牌交換授權,並且能夠頒發令牌,允許資源伺服器充當用戶端並模擬最終使用者。

結論

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

提示:請參閱 RFC 8693附錄 A,以取得其他範例,包括也支援的另一個用例「委派」的範例。

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

取得 Spring 電子報

保持與 Spring 電子報的聯繫

訂閱

領先一步

VMware 提供培訓和認證,以加速您的進展。

了解更多

取得支援

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

了解更多

即將到來的活動

查看 Spring 社群中所有即將到來的活動。

查看全部