Merge pull request #11759 from hmdrzsharifi/BAEL-5300

Bael 5300: Provide an OAuth2 Token to a Feign Client
This commit is contained in:
Greg Martin 2022-02-16 16:47:11 -05:00 committed by GitHub
commit c4065e80b4
9 changed files with 259 additions and 1 deletions

View File

@ -49,6 +49,14 @@
<groupId>io.github.openfeign.form</groupId>
<artifactId>feign-form-spring</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-oauth2-client</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>

View File

@ -0,0 +1,16 @@
package com.baeldung.cloud.openfeign.client;
import com.baeldung.cloud.openfeign.model.Payment;
import com.baeldung.cloud.openfeign.oauthfeign.OAuthFeignConfig;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import java.util.List;
@FeignClient(name = "payment-client", url = "http://localhost:8081/resource-server-jwt", configuration = OAuthFeignConfig.class)
public interface PaymentClient {
@RequestMapping(value = "/payments", method = RequestMethod.GET)
List<Payment> getPayments();
}

View File

@ -0,0 +1,24 @@
package com.baeldung.cloud.openfeign.controller;
import com.baeldung.cloud.openfeign.client.PaymentClient;
import com.baeldung.cloud.openfeign.model.Payment;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
@RestController
public class PaymentController {
private final PaymentClient paymentClient;
public PaymentController(PaymentClient paymentClient) {
this.paymentClient = paymentClient;
}
@GetMapping("/payments")
public List<Payment> getPayments() {
List<Payment> payments = paymentClient.getPayments();
return payments;
}
}

View File

@ -0,0 +1,23 @@
package com.baeldung.cloud.openfeign.model;
public class Payment {
private String id;
private double amount;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public double getAmount() {
return amount;
}
public void setAmount(double amount) {
this.amount = amount;
}
}

View File

@ -0,0 +1,19 @@
package com.baeldung.cloud.openfeign.oauthfeign;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
@Configuration
public class OAuth2WebSecurityConfigurerAdapter extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf()
.disable()
.oauth2Client();
http
.authorizeRequests().anyRequest().permitAll();
}
}

View File

@ -0,0 +1,85 @@
package com.baeldung.cloud.openfeign.oauthfeign;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.oauth2.client.OAuth2AuthorizeRequest;
import org.springframework.security.oauth2.client.OAuth2AuthorizedClient;
import org.springframework.security.oauth2.client.OAuth2AuthorizedClientManager;
import org.springframework.security.oauth2.client.registration.ClientRegistration;
import java.util.Collection;
import java.util.Collections;
import static java.util.Objects.isNull;
public class OAuthClientCredentialsFeignManager {
private static final Logger logger = LoggerFactory.getLogger(OAuthClientCredentialsFeignManager.class);
private final OAuth2AuthorizedClientManager manager;
private final Authentication principal;
private final ClientRegistration clientRegistration;
public OAuthClientCredentialsFeignManager(OAuth2AuthorizedClientManager manager, ClientRegistration clientRegistration) {
this.manager = manager;
this.clientRegistration = clientRegistration;
this.principal = createPrincipal();
}
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();
}
};
}
public String getAccessToken() {
try {
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");
}
return client.getAccessToken().getTokenValue();
} catch (Exception exp) {
logger.error("client credentials error " + exp.getMessage());
}
return null;
}
}

View File

@ -0,0 +1,46 @@
package com.baeldung.cloud.openfeign.oauthfeign;
import feign.RequestInterceptor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.oauth2.client.*;
import org.springframework.security.oauth2.client.registration.ClientRegistration;
import org.springframework.security.oauth2.client.registration.ClientRegistrationRepository;
@Configuration
public class OAuthFeignConfig {
public static final String CLIENT_REGISTRATION_ID = "keycloak";
private final OAuth2AuthorizedClientService oAuth2AuthorizedClientService;
private final ClientRegistrationRepository clientRegistrationRepository;
public OAuthFeignConfig(OAuth2AuthorizedClientService oAuth2AuthorizedClientService,
ClientRegistrationRepository clientRegistrationRepository) {
this.oAuth2AuthorizedClientService = oAuth2AuthorizedClientService;
this.clientRegistrationRepository = clientRegistrationRepository;
}
@Bean
public RequestInterceptor requestInterceptor() {
ClientRegistration clientRegistration = clientRegistrationRepository.findByRegistrationId(CLIENT_REGISTRATION_ID);
OAuthClientCredentialsFeignManager clientCredentialsFeignManager =
new OAuthClientCredentialsFeignManager(authorizedClientManager(), clientRegistration);
return requestTemplate -> {
requestTemplate.header("Authorization", "Bearer " + clientCredentialsFeignManager.getAccessToken());
};
}
@Bean
OAuth2AuthorizedClientManager authorizedClientManager() {
OAuth2AuthorizedClientProvider authorizedClientProvider = OAuth2AuthorizedClientProviderBuilder.builder()
.clientCredentials()
.build();
AuthorizedClientServiceOAuth2AuthorizedClientManager authorizedClientManager =
new AuthorizedClientServiceOAuth2AuthorizedClientManager(clientRegistrationRepository, oAuth2AuthorizedClientService);
authorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider);
return authorizedClientManager;
}
}

View File

@ -1,3 +1,10 @@
server.port=8085
spring.main.allow-bean-definition-overriding=true
spring.application.name= openfeign
logging.level.com.baeldung.cloud.openfeign.client: DEBUG
feign.hystrix.enabled=true
spring.security.oauth2.client.registration.keycloak.authorization-grant-type=client_credentials
spring.security.oauth2.client.registration.keycloak.client-id=payment-app
spring.security.oauth2.client.registration.keycloak.client-secret=863e9de4-33d4-4471-b35e-f8d2434385bb
spring.security.oauth2.client.provider.keycloak.token-uri=http://localhost:8083/auth/realms/master/protocol/openid-connect/token

View File

@ -0,0 +1,30 @@
package com.baeldung.cloud.openfeign;
import com.baeldung.cloud.openfeign.client.PaymentClient;
import com.baeldung.cloud.openfeign.model.Payment;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import java.util.List;
import static org.junit.Assert.assertFalse;
@RunWith(SpringRunner.class)
@SpringBootTest
public class PaymentClientUnitTest {
@Autowired
private PaymentClient paymentClient;
@Test
public void whenGetPayment_thenListPayments() {
List<Payment> payments = paymentClient.getPayments();
assertFalse(payments.isEmpty());
}
}