spring-security-oauth2:2.4.0.RELEASE
クラスでは、OAuth2RestTemplate
、OAuth2ProtectedResourceDetails
、ClientCredentialsAccessTokenProvider
などのクラスはすべて非推奨としてマークされています。
これらのクラスのjavadocから、 Spring Security Migration Guide が示されています。これは、コアSpring-Security 5プロジェクトに移行する必要があることを示唆しています。ただし、このプロジェクトでユースケースを実装する方法を見つけるのに苦労しています。
すべてのドキュメントと例は、サードパーティとの統合について述べていますOAuthアプリケーションへの受信リクエストを認証してサードパーティを使用したい場合OAuth本人確認のためのプロバイダー。
私のユースケースでは、OAuthで保護されている外部サービスに対してRestTemplate
を使用してリクエストを行うだけです。現在、私はOAuth2ProtectedResourceDetails
に渡すクライアントIDとシークレットを使用してOAuth2RestTemplate
を作成しています。カスタムClientCredentialsAccessTokenProvider
をOAuth2ResTemplate
に追加しました。これは、使用しているOAuthプロバイダーが必要とするトークンリクエストに追加のヘッダーを追加するだけです。
Spring-Security 5のドキュメントで、 トークンリクエストのカスタマイズ に言及しているセクションを見つけましたが、これも、サードパーティで着信リクエストを認証するコンテキストにあるようですOAuthプロバイダー。これをClientHttpRequestInterceptor
のようなものと組み合わせて使用して、外部サービスへの各送信リクエストが最初にトークンを取得し、次にそれをリクエストに追加することを保証する方法は明確ではありません。
また、上記のリンクされた移行ガイドには、インターセプターでの使用に役立つと記載されているOAuth2AuthorizedClientService
への参照がありますが、これはClientRegistrationRepository
のようなものに依存しているようですを使用して着信リクエストが認証されるようにする場合は、サードパーティプロバイダーの登録を維持します。
アプリケーションからの発信要求に追加するトークンを取得するために、OAuthプロバイダーを登録するためにSpring-Security 5の新機能を利用できる方法はありますか?
こんにちは多分手遅れかもしれませんが、RestTemplateはまだSpring Security 5でサポートされています非反応のアプリRestTemplateはまだ使用されています必要なことは、Spring Securityを適切に構成し、移行ガイドに記載されているインターセプターを作成することだけです。
Client_credentialsフローを使用するには、次の構成を使用します
application.yml
spring:
security:
oauth2:
resourceserver:
jwt:
jwk-set-uri: ${okta.oauth2.issuer}/v1/keys
client:
registration:
okta:
client-id: ${okta.oauth2.clientId}
client-secret: ${okta.oauth2.clientSecret}
scope: "custom-scope"
authorization-grant-type: client_credentials
provider: okta
provider:
okta:
authorization-uri: ${okta.oauth2.issuer}/v1/authorize
token-uri: ${okta.oauth2.issuer}/v1/token
OauthResTemplateの構成
@Configuration
@RequiredArgsConstructor
public class OAuthRestTemplateConfig {
public static final String OAUTH_WEBCLIENT = "OAUTH_WEBCLIENT";
private final RestTemplateBuilder restTemplateBuilder;
private final OAuth2AuthorizedClientService oAuth2AuthorizedClientService;
private final ClientRegistrationRepository clientRegistrationRepository;
@Bean(OAUTH_WEBCLIENT)
RestTemplate oAuthRestTemplate() {
var clientRegistration = clientRegistrationRepository.findByRegistrationId(Constants.OKTA_AUTH_SERVER_ID);
return restTemplateBuilder
.additionalInterceptors(new OAuthClientCredentialsRestTemplateInterceptorConfig(authorizedClientManager(), clientRegistration))
.setReadTimeout(Duration.ofSeconds(5))
.setConnectTimeout(Duration.ofSeconds(1))
.build();
}
@Bean
OAuth2AuthorizedClientManager authorizedClientManager() {
var authorizedClientProvider = OAuth2AuthorizedClientProviderBuilder.builder()
.clientCredentials()
.build();
var authorizedClientManager = new AuthorizedClientServiceOAuth2AuthorizedClientManager(clientRegistrationRepository, oAuth2AuthorizedClientService);
authorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider);
return authorizedClientManager;
}
}
インターセプター
public class OAuthClientCredentialsRestTemplateInterceptor implements ClientHttpRequestInterceptor {
private final OAuth2AuthorizedClientManager manager;
private final Authentication principal;
private final ClientRegistration clientRegistration;
public OAuthClientCredentialsRestTemplateInterceptor(OAuth2AuthorizedClientManager manager, ClientRegistration clientRegistration) {
this.manager = manager;
this.clientRegistration = clientRegistration;
this.principal = createPrincipal();
}
@Override
public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution) throws IOException {
OAuth2AuthorizeRequest oAuth2AuthorizeRequest = OAuth2AuthorizeRequest
.withClientRegistrationId(clientRegistration.getRegistrationId())
.principal(principal)
.build();
OAuth2AuthorizedClient client = manager.authorize(oAuth2AuthorizeRequest);
if (isNull(client)) {
throw new IllegalStateException("client credentials flow on " + clientRegistration.getRegistrationId() + " failed, client is null");
}
request.getHeaders().add(HttpHeaders.AUTHORIZATION, BEARER_PREFIX + client.getAccessToken().getTokenValue());
return execution.execute(request, body);
}
private Authentication createPrincipal() {
return new Authentication() {
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return Collections.emptySet();
}
@Override
public Object getCredentials() {
return null;
}
@Override
public Object getDetails() {
return null;
}
@Override
public Object getPrincipal() {
return this;
}
@Override
public boolean isAuthenticated() {
return false;
}
@Override
public void setAuthenticated(boolean isAuthenticated) throws IllegalArgumentException {
}
@Override
public String getName() {
return clientRegistration.getClientId();
}
};
}
}
これにより、最初の呼び出し時とトークンの有効期限が切れたときにaccess_tokenが生成されます。 OAuth2AuthorizedClientManagerがこれをすべて管理します
@matt Williamsの回答は非常に役に立ちました。誰かがプログラムでclientIdとWebClient構成のシークレットを渡したい場合に備えて追加します。これがどのようにしてできるかです。
@Configuration
public class WebClientConfig {
public static final String TEST_REGISTRATION_ID = "test-client";
@Bean
public ReactiveClientRegistrationRepository clientRegistrationRepository() {
var clientRegistration = ClientRegistration.withRegistrationId(TEST_REGISTRATION_ID)
.authorizationGrantType(AuthorizationGrantType.CLIENT_CREDENTIALS)
.clientId("<client_id>")
.clientSecret("<client_secret>")
.tokenUri("<token_uri>")
.build();
return new InMemoryReactiveClientRegistrationRepository(clientRegistration);
}
@Bean
public WebClient testWebClient(ReactiveClientRegistrationRepository clientRegistrationRepo) {
var oauth = new ServerOAuth2AuthorizedClientExchangeFilterFunction(clientRegistrationRepo, new UnAuthenticatedServerOAuth2AuthorizedClientRepository());
oauth.setDefaultClientRegistrationId(TEST_REGISTRATION_ID);
return WebClient.builder()
.baseUrl("https://.test.com")
.filter(oauth)
.defaultHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE);
}
}