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

工程 | 史蒂夫·裡森伯格 | 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 的受眾。顯然,我們不能將對 user-service 的請求中的令牌重新用於對 message-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 構建一個全新的授權伺服器應用程式來支援此流程。

使用 Spring InitializrOAuth2 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 引數的令牌交換授權,並且能夠頒發令牌,允許資源伺服器充當客戶端並模擬終端使用者。

結論

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

提示:有關更多示例,包括也受支援的“委託”用例示例,請參閱 RFC 8693附錄 A

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

獲取 Spring 新聞通訊

透過 Spring 新聞通訊保持聯絡

訂閱

領先一步

VMware 提供培訓和認證,助您加速進步。

瞭解更多

獲得支援

Tanzu Spring 提供 OpenJDK™、Spring 和 Apache Tomcat® 的支援和二進位制檔案,只需一份簡單的訂閱。

瞭解更多

即將舉行的活動

檢視 Spring 社群所有即將舉行的活動。

檢視所有