I have two Spring Boot services, S1 and S2, both with their own API endpoints. S1 is protected with OAuth2 client credentials, so I have configured S2 as an OAuth2 client and am using WebClient
to make API requests on S2. To achieve this, I added two dependencies to my S2 application
spring-boot-starter-oauth2-client
andspring-boot-starter-webflux
.
The configuration of WebClient
and my application.properties
file is as follows:
spring:
security:
oauth2:
client:
registration:
liferayclient:
client-id: ${CLIENT_ID:bcvc7yt7}
client-secret: ${CLIENT_SECRET:vhsvchc76}
authorization-grant-type: client_credentials
provider:
liferayclient:
token-uri: localhost:8080/o/oauth2/token
The WebClient
configuration is:
@Configuration
public class WebClientConfig {
@Bean
WebClient webClient(AuthorizedClientServiceReactiveOAuth2AuthorizedClientManager authorizedClientManager) {
ExchangeStrategies exchangeStrategies = ExchangeStrategies.builder()
.codecs(configurer -> configurer.defaultCodecs().maxInMemorySize(16 * 1024 * 1024))
.build();
ServerOAuth2AuthorizedClientExchangeFilterFunction oauth2Client =
new ServerOAuth2AuthorizedClientExchangeFilterFunction(authorizedClientManager);
oauth2Client.setDefaultClientRegistrationId("liferayclient");
HttpClient httpClient = HttpClient.create()
.option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 10 * 1000)
.doOnConnected(connection -> {
connection.addHandlerLast(new ReadTimeoutHandler(2 * 60 * 1000L, MILLISECONDS));
connection.addHandlerLast(new WriteTimeoutHandler(2 * 60 * 1000L, MILLISECONDS));
});
return WebClient.builder()
.filter(oauth2Client)
.clientConnector(new ReactorClientHttpConnector(httpClient))
.exchangeStrategies(exchangeStrategies)
.build();
}
@Bean
ReactiveClientRegistrationRepository clientRegistrations(
@Value("${spring.security.oauth2.client.provider.liferayclient.token-uri}") String token_uri,
@Value("${spring.security.oauth2.client.registration.liferayclient.client-id}") String client_id,
@Value("${spring.security.oauth2.client.registration.liferayclient.client-secret}") String client_secret,
@Value("${spring.security.oauth2.client.registration.liferayclient.authorization-grant-type}") String authorizationGrantType
) {
ClientRegistration registration = ClientRegistration
.withRegistrationId("liferayclient")
.tokenUri(token_uri)
.clientId(client_id)
.clientSecret(client_secret)
.authorizationGrantType(new AuthorizationGrantType(authorizationGrantType))
.build();
return new InMemoryReactiveClientRegistrationRepository(registration);
}
@Bean
public AuthorizedClientServiceReactiveOAuth2AuthorizedClientManager authorizedClientManager(
ReactiveClientRegistrationRepository clientRegistrationRepository) {
InMemoryReactiveOAuth2AuthorizedClientService clientService =
new InMemoryReactiveOAuth2AuthorizedClientService(clientRegistrationRepository);
ReactiveOAuth2AuthorizedClientProvider authorizedClientProvider =
ReactiveOAuth2AuthorizedClientProviderBuilder.builder().clientCredentials().build();
AuthorizedClientServiceReactiveOAuth2AuthorizedClientManager authorizedClientManager =
new AuthorizedClientServiceReactiveOAuth2AuthorizedClientManager(
clientRegistrationRepository, clientService);
authorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider);
return authorizedClientManager;
}
}
Now coming to the issue, I am able to successfully make an API call on S1 from S2 using a web client. However, I am encountering an issue where I am unable to call any API on the service S2. Whenever I attempt to make an API call on S2, I receive a "login with OAuth 2.0" prompt on the web browser. The HTML response I am getting is as folows.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<meta name="description" content="">
<meta name="author" content="">
<title>Please sign in</title>
<link href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-beta/css/bootstrap.min.css" rel="stylesheet"
integrity="sha384-/Y6pD6FV/Vv2HJnA6t+vslU6fwYXjCFtcEpHbNJ0lyAFsXTsjBbfaDjzALeQsN6M" crossorigin="anonymous">
<link href="https://getbootstrap.com/docs/4.0/examples/signin/signin.css" rel="stylesheet"
crossorigin="anonymous" />
</head>
<body>
<div class="container">
<h2 class="form-signin-heading">Login with OAuth 2.0</h2>
<table class="table table-striped">
</table>
</div>
</body>
</html>
Before adding the spring-boot-starter-oauth2-client
dependency, I was able to make these API calls using Postman without any issues. It appears that since the dependency was added, Spring now expects that the current microservice is also protected by OAuth2. As a result, any REST API created in the current microservice will automatically be protected by OAuth2.
I am seeking advice on how to address this issue and enable outgoing calls to require OAuth2 authorization, while allowing incoming calls to bypass this requirement. I am utilizing Spring Boot version 3.0.0 and one of the solution that I have tried is as follows.
package com.report.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.web.SecurityFilterChain;
@Configuration
public class SecurityConfig{
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception{
return http
.csrf().disable()
.authorizeHttpRequests()
.requestMatchers("*").permitAll()
.and()
.build();
}
}
I tried to create a custom SecurityFilterChain
bean that permits all HTTP requests, but it did not help me. Although I am able to make API calls from S2 to S1 with the WebClient
like previously, I am still unable to make any API calls on S2 via Postman. I am now getting a Forbidden error after adding this.