I need to call an external API from my spring boot project.
The external API is using OAuth 2 security authentication using client_credentials.
I have the client id and secret key. Can RestTemplate be used?
Can you suggest how to pass the id and key as part of the API call? Any example?
You can use RestTemplate too but I would suggest that WebClient will be more better as it is reactive and asynchronous and RestTemplate may be depreciated for future use (maybe not too).
Please refer to https://www.baeldung.com/spring-webclient-oauth2
#Autowired
WebClient client;
public Mono<String> obtainSecuredResource() {
String encodedClientData =
Base64Utils.encodeToString("bael-client-id:bael-secret".getBytes());
Mono<String> resource = client.post()
.uri("localhost:8085/oauth/token")
.header("Authorization", "Basic " + encodedClientData)
.body(BodyInserters.fromFormData("grant_type", "client_credentials"))
.retrieve()
.bodyToMono(JsonNode.class)
.flatMap(tokenResponse -> {
String accessTokenValue = tokenResponse.get("access_token")
.textValue();
return client.get()
.uri("localhost:8084/retrieve-resource")
.headers(h -> h.setBearerAuth(accessTokenValue))
.retrieve()
.bodyToMono(String.class);
});
return resource.map(res ->
"Retrieved the resource using a manual approach: " + res);
}
Related
I am working on building a service in GRPC using .NET core. The scenario is as follows - there are 2 services - A and B.
Service A exposes a REST API.
Service B exposes GRPC API.
User invokes Service A, and Service A invokes Service B. I need to pass the headers sent by the user to Service B.
How can I do this with GRPC? The Interceptor interface does not seem to expose the Metadata collection.
To forward the headers sent from client, on the rest api you can do something like this:
using var channel = GrpcChannel.ForAddress("http://localhost:5005");
var client = new Greeter.GreeterClient(channel);
Metadata headers = new()
{
{ "myCustomHeader", myCustomHeader }
};
var reply = await client.SayHelloAsync(new HelloRequest { Name = model.Name }, headers);
Using the Metadata you can pass all headers that you need.
On the gRPC service side, you need to use HttpContextAccessor here
This way you can read the http context request headers
string header = _httpContextAccessor.HttpContext.Request.Headers["myCustomHeader"];
take a look at this repo: here
I am trying to create microservices using Spring-boot Java and SteelToe ASP.NET
Step-1: I created a full service using Java (A service with UI and API. It is hosted on PCF). The API has ClassesControler defined inside.
Step-2: Create a microservice using ASP.NET, SteelToe. Register the service in Eureka and make it discoverable using Zuul.
Step-3: Use the Interface, Service approach to access the JAVA microservice(s)
namespace employee-client.Service
{
public interface IRelayService
{
Task<HttpResponseMessage> getClassesList(string relativeUrl = "/api/v1/classes");
}
}
Service with Implementation for Interface:
namespace employee-client.Service
{
public class RelayService : IRelayService
{
DiscoveryHttpClientHandler _handler;
string _accessToken;
private const string BASE_URL = "https://www.example.com";
public QortaService(IDiscoveryClient client, string accessToken)
{
_handler = new DiscoveryHttpClientHandler(client);
_accessToken = accessToken;
}
public async Task<HttpResponseMessage> getClassesList(string relativeUrl)
{
string classesUrl= BASE_URL + relativeUrl;
HttpClient client = GetClient();
HttpRequestMessage request = new HttpRequestMessage();
request.RequestUri = new Uri(classesUrl);
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", _accessToken);
return await client.SendAsync(request, HttpCompletionOption.ResponseContentRead);
}
private HttpClient GetClient()
{
var client = new HttpClient(_handler, false);
return client;
}
}
}
I came up with this approach based on the example in SteelToe but I hate hardcoding the BASE_URL.
Question: I very much like the #FeignClient annotation approach used in Java. Any ideas about how I can access an existing microservice in a better way. If so, an example would be much appreciated
Edit:
I modified the question to make more clear.
The flow of traffic is from Java Service to .NET service. .NET service requests for a list of classes from the controller in JAVA service (ClassesController.java)
I'm unclear which direction traffic is flowing in your scenario, but I think you're saying the .NET application is trying to call the Java application. The code you're using is from before HttpClientFactory was introduced and is a bit clunkier than what's possible now in general. Steeltoe can be used with HttpClientFactory for a better overall experience.
Steeltoe has debug logging available to confirm the results of service lookup if you set logging:loglevel:Steeltoe.Common.Discovery = true in your application config.
You didn't mention specifically what isn't working, but I'm guessing you're getting a 404 since it looks like your code will create a request path looking like https://fortuneService/api/fortunes/random/api/v1/classes
If you're looking for something like Feign in .NET, you could try out DHaven.Faux
For others who are looking for the same:
namespace employee-client.Service
{
public class RelayService : IRelayService
{
private const string CLASSES_API_SERVICEID = "classes-api";
IDiscoveryClient _discoveryClient;
DiscoveryHttpClientHandler _handler;
string _accessToken;
public RelayService(IDiscoveryClient discoveryClient, string accessToken)
{
_discoveryClient = discoveryClient;
_handler = new DiscoveryHttpClientHandler(client);
_accessToken = accessToken;
}
public async Task<HttpResponseMessage> getClassesList()
{
var classesApiInstances = _discoveryClient.GetInstances(CLASSES_API_SERVICEID);
Uri classesApiUri = classesApiInstances[0].Uri;
string classesUrl= classesApiUri.AbsoluteUri + relativeUrl;
HttpClient httpClient = GetClient();
HttpRequestMessage request = new HttpRequestMessage();
request.RequestUri = new Uri(classesUrl);
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", _accessToken);
return await httpClient.SendAsync(request, HttpCompletionOption.ResponseContentRead);
}
private HttpClient GetClient()
{
var client = new HttpClient(_handler, false);
return client;
}
}
}
I am integrating with WebSockets in my Spring MVC application. The authentication mechanism for my application is OAuth.
I was able to pass my OAuth token in connection string when connecting to SockJS:
var webSocketUrl = '/websocket' + '?access_token=' + auth.access_token;
var socket = new SockJS(webSocketUrl);
var stompClient = Stomp.over(socket);
Now I can send messages and subscribe to STOMP channels:
stompClient.connect({}, function(frame) {
stompClient.subscribe('/topic/greetings', function(greeting){
console.log(greeting);
});
stompClient.send("/app/hello", {}, JSON.stringify('John'));
});
In my backend I am able to get user principle injected to my STOMP controller methods (which means that Spring understands that there is an OAuth token in connection string):
#Controller
public class MyWebsocketsController {
#MessageMapping("/hello")
#SendTo("/topic/greetings")
public String greet(String name, Principal authorizedUser) {
return "Hello, " + name + ", you have authorized as " + authorizedUser.getName();
}
}
Now I would like to require user authorization on all messages and subscriptions, i.e. I would like to make sure that all calls to web sockets return 403 error code if no valid token was provided when connecting to SockJS.
I add this security configuration to my project:
public class WebSocketSecurityConfig extends AbstractSecurityWebSocketMessageBrokerConfigurer {
#Override
protected void configureInbound(MessageSecurityMetadataSourceRegistry messages) {
messages
.simpTypeMatchers(CONNECT, HEARTBEAT, UNSUBSCRIBE, DISCONNECT).permitAll()
.simpDestMatchers("/app/**").authenticated()
.simpSubscribeDestMatchers("/topic/**").authenticated()
.simpTypeMatchers(MESSAGE, SUBSCRIBE).denyAll()
.anyMessage().denyAll();
}
}
But it does not seem to do the job. If I remove access token from connection string, I am still able to send messages to controller and to subscribe to channel.
// This stil works:
var webSocketUrl = '/websocket'; // + '?access_token=' + auth.access_token;
Of course, now I can't get the user principle in my controller, but except for this web sockets work fine.
I would appreciate any ideas how to make this thing work or any explanation why web sockets security configuration is not working for my case. What am I missing? Thanks.
What is the best way to inspect the Request Headers for a service endpoint?
ContactService : Service
Having read this https://github.com/ServiceStack/ServiceStack/wiki/Access-HTTP-specific-features-in-services I'm curious as to the preferred way to get to the Interface.
Thank you,
Stephen
Inside a ServiceStack Service you can access the IHttpRequest and IHttpResponse objects with:
public class ContactService : Service
{
public object Get(Contact request)
{
var headerValue = base.Request.Headers[headerKey];
//or the same thing via a more abstract (and easier to Mock):
var headerValue = base.RequestContext.GetHeader(headerKey);
}
}
The IHttpRequest is a wrapper over the underlying ASP.NET HttpRequest or HttpListenerRequest (depending if you're hosting on ASP.NET or self-hosted HttpListener). So if you're running in ASP.NET you can get the underlying ASP.NET HttpRequest with:
var aspnetRequest = (HttpRequest)base.Request.OriginalRequest;
var headerValue = aspnetRequest.Headers[headerKey];
I'm trying to call the Recurly REST API using the Hammock C# library for .NET. The API calls require an Authorization header on the HttpRequest, and the authentication scheme is Basic authentication with the Base64 encoded API key in the username portion of the header. I thought that I could create a new Hammock BasicAuthCredentials object with the encoded key in the Username property of the object, then assign the object to the Credentials property of either the RestClient or RestRequest objects. However, this does not seem to generate an Authorization header on the outbound HttpRequest.
If I add the Authorization header manually using the AddHeader method on one of those objects, the API call succeeds. If I use the Credentials property with the BasicAuthCredentials object, I get an Access Denied error from Recurly.
This seems pretty basic, so I know I'm doing something wrong. So, in Hammock, is the Credentials property on either the RestClient or RestRequest object supposed to create an Authorization header on the Http request?
Thanks for any help from a super Hammock user!
The code that fails:
class Program
{
public static void Main(string[] args)
{
string encodedAPIKey = Convert.ToBase64String(Encoding.UTF8.GetBytes("APIKeyHere"));
BasicAuthCredentials credentials = new BasicAuthCredentials
{
Username = encodedAPIKey
};
RestClient client = new RestClient
{
Authority = "https://api.recurly.com",
VersionPath = "v2"
};
client.AddHeader("Accept", "application/xml");
RestRequest request = new RestRequest
{
Credentials = credentials,
Path = "plans"
};
RestResponse response = client.Request(request);
Console.WriteLine(response.Content);
Console.ReadLine();
}
}
The code that succeeds:
class Program
{
public static void Main(string[] args)
{
string encodedAPIKey = Convert.ToBase64String(Encoding.UTF8.GetBytes("APIKeyHere"));
RestClient client = new RestClient
{
Authority = "https://api.recurly.com",
VersionPath = "v2"
};
client.AddHeader("Accept", "application/xml");
client.AddHeader("Authorization", "Basic " + encodedAPIKey);
RestRequest request = new RestRequest
{
Path = "plans"
};
RestResponse response = client.Request(request);
Console.WriteLine(response.Content);
Console.ReadLine();
}
}
After getting no answers to my question, I did a search for alternative Rest libraries for .NET and found RestSharp. I was able to get it working with Recurly using its built-in Basic Authorization implementation on the first try, so I will be implementing using RestSharp. The code looks very similar, so the migration should be an easy one.