Set custom Basic Auth Header to RestTemplate - java

I have rest template config to use restTemplate for calling 3rd Party Service API. That 3rd Party Service API needs only Basic Auth from security. So in general it looks like this
My local uri i.e. localhost:8082/api/caller -> restTemplate.getForEntity(exact 3rd party service API URL). The issue is that on restTemplate.getForEntity step, my headers are empty, but it's not a problem, I can set it using interceptor, like this
#Configuration
public class RestTemplateConfig {
#NonNull
public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution) throws IOException {
HttpHeaders headers = request.getHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
headers.set("Authorization", "Basic c3VHaWxzNFlBaExZNEg6NWtwQTJuV1AAAA3YXVOd1FWWXVkTEdNjoRrQks0MUVjY1hNa3VRYUdSdE1VMDdyWUtpclNycDIzcGtASktuRQ==");
return execution.execute(request, body);
}
#Bean
public RestTemplate restTemplate() {
final RestTemplate restTemplate = new RestTemplate();
final MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter();
converter.setObjectMapper(new ObjectMapper());
restTemplate.getMessageConverters().add(converter);
restTemplate.getInterceptors().add(this::intercept);
return restTemplate;
}
}
As you see, I've hardcoded value Authorization, but I need to have to get it retrieved automatically for every request. How to make it available for restTemplate too?

Related

How to add multiple authorization headers in spring boot resttemplate

I have the following common configuration in my Spring Boot application:
private RestTemplate getRestTemplate(String username, String pwd){
RestTemplate restTemplate = new RestTemplate(new HttpComponentsClientHttpRequestFactory());
restTemplate.getInterceptors().add(new BasicAuthenticationInterceptor(username, pwd));
return restTemplate;
}
Right now I am using BasicAuthenticationInterceptor to add basic auth credentials to the Http 'Authorization' headers.
My new requirement asks to add another Http 'Authorization' header with a OAuth/JWT token. So I added something like this below:
headers.set(HttpHeaders.AUTHORIZATION, escape(token));
But as I was using Spring's BasicAuthenticationInterceptor when I add token as "Authorization" header because of the if condition in the spring BasicAuthenticationInterceptor class it is not adding the basic auth credentials. Please find below if condition for more info:
public class BasicAuthenticationInterceptor implements ClientHttpRequestInterceptor {
private final String encodedCredentials;
public BasicAuthenticationInterceptor(String username, String password) {
this(username, password, (Charset)null);
}
public BasicAuthenticationInterceptor(String username, String password, #Nullable Charset charset) {
this.encodedCredentials = HttpHeaders.encodeBasicAuth(username, password, charset);
}
public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution) throws IOException {
HttpHeaders headers = request.getHeaders();
if (!headers.containsKey("Authorization")) { /* here it will not add if I already have an Authorization header*/
headers.setBasicAuth(this.encodedCredentials);
}
return execution.execute(request, body);
}
}
So I was thinking may be I need to write my own custom Interceptor but before doing so I wanted to see if there is already a existing Interceptor that can fulfill my request. BTW, I was hoping to use BasicAuthorizationInterceptor but is deprecated in 5.3.9(my current spring version).
And in a sidenote, if I am going to write a new interceptor (if that is what you suggest.) then I also wanted to add the token auth header in that custom interceptor.
Any input or suggestion is appreciated.

Spring WebClient can't read request body before send with ExchangeFilterFunction

All the time I used RestTemplate and decided to switch to WebClient.
Before sending a request, I sign request body with a private key and the client checks the request with a public one.
My interceptor:
private static class SignatureClientHttpRequestInterceptor implements ClientHttpRequestInterceptor {
private final PrivateKey privateKey;
private SignatureClientHttpRequestInterceptor(String privateKeyLocation) {
this.privateKey = PemUtils.getPrivateKey(Paths.get(privateKeyLocation));
}
#Override
public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution) throws IOException {
if (request.getMethod() == HttpMethod.POST) {
request.getHeaders().add("X-Signature", Base64.getEncoder().encodeToString(PemUtils.signData(privateKey, SignatureAlgorithm.RS256.getJcaName(), body)));
}
return execution.execute(request, body);
}
}
But at WebClient I did not find such an opportunity in ExchangeFilterFunction.
Is there anyway to do this in WebClient or do I have to manually sign the request body before sending it?
Signing the body would require it in serialized form, but serialization happens just before sending the data so it needs to be intercepted somehow.
In the case of JSON content, you can create your own Encoder (wrapping the existing Jackson2JsonEncoder for example) and passing this as an ExchangeStrategies when building the WebClient. After the serialized data is intercepted, you can inject the headers. But the Encoder does not have a reference to the ClientHttpRequest so you will need to capture this object in an HttpConnector and pass it in the SubscriberContext.
This blog post explains the process: https://andrew-flower.com/blog/Custom-HMAC-Auth-with-Spring-WebClient#s-post-data-signing
As an example, your WebClient creation step might look like below, where MessageCapturingHttpConnector is a connector that captures the ClientHttpRequest and BodyCapturingJsonEncoder
Signer signer = new Signer(clientId, secret);
MessageSigningHttpConnector httpConnector = new MessageSigningHttpConnector();
BodyCapturingJsonEncoder bodyCapturingJsonEncoder
= new BodyCapturingJsonEncoder(signer);
WebClient client
= WebClient.builder()
.exchangeFunction(ExchangeFunctions.create(
httpConnector,
ExchangeStrategies
.builder()
.codecs(clientDefaultCodecsConfigurer -> {
clientDefaultCodecsConfigurer.defaultCodecs().jackson2JsonEncoder(bodyCapturingJsonEncoder);
clientDefaultCodecsConfigurer.defaultCodecs().jackson2JsonDecoder(new Jackson2JsonDecoder(new ObjectMapper(), MediaType.APPLICATION_JSON));
})
.build()
))
.baseUrl(String.format("%s://%s/%s", environment.getProtocol(), environment.getHost(), environment.getPath()))
.defaultHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)
.build();

Spring Rest template overwriting Authorization header value

I am making rest call like below:
REST_TEMPLATE.exchange(
external_rest_url,
HttpMethod.POST,
new HttpEntity<>(dto, getHeaders()),
Map.class)
and my headers are as below:
private HttpHeaders getHeaders() {
HttpHeaders headers = new HttpHeaders();
headers.setAccept(Collections.singletonList(MediaType.APPLICATION_JSON));
headers.setContentType(MediaType.APPLICATION_JSON);
headers.set("User-Agent","Spring's RestTemplate");
headers.set(HttpHeaders.AUTHORIZATION, "some value");
return headers;
}
when I run my code the header HttpHeaders.AUTHORIZATION is getting replaced with undefined
See request header in snapshot below from network logs:
Do anyone know why spring is behaving like this or specifically spring-web:5.0.5 jar. I tried changing the version of jar as well but result is same.
Springboot version I use is 2.0.x.
you can add an interceptor to your RestTemplate if you need to add the same headers to all requests:
public void sampleHeader(final RestTemplate restTemplate){
//Add a ClientHttpRequestInterceptor to the RestTemplate
restTemplate.getInterceptors().add(new ClientHttpRequestInterceptor(){
#Override
public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution) throws IOException {
request.getHeaders().set(HttpHeaders.AUTHORIZATION, "some value");//Set the header for each request
return execution.execute(request, body);
}
});
}

Spring Boot : Token authentication(bearer) in request headers in rest api when token also comes from calling another api

In my spring boot Application i have a scheduler which calls an API to generate token which expires in 15 min. Time of scheduler is also 15 min. please find below sample:
public class TokenGeneration {
private static String token = null;
#Scheduled(15 minutes)
public String fetchToken() {
// api call which return token
HttpEntity<model> response = restTemplate.exchange(uri, POST, entity, model.class);
token = response.getBody().getAccessToken();
}
}
I stored token value in static variable from a non static method so that i can use this token variable wherever i want to use token value. is this right approach ? if not plz let me know how i can achieve this.
Do i need to make TokenGeneration class singleton so that only one instance of this class is made throught application?
Also i want to create an interceptor or filter in which i can set Authorization headers and token value so that each request will populate authorization header automatically, i don't want to set authorization header in each request like this :
HttpHeaders headers = new HttpHeaders();
headers.set(CpsConstant.AUTHORIZATION, CpsConstant.BEARER + token);
So i tried with this custom interceptor :
public class RestTemplateInterceptor implements ClientHttpRequestInterceptor{
#Override
public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution) throws IOException {
String token = TokenGeneration.token;
request.getHeaders().add("Authorization", "Bearer " + token);
return execution.execute(request, body);
}
will add this interceptor in restTemplate in config file.
So is this right approach for both token generation as well as setting headers for each request or any improvements need to be done in this approach ?
Me thinking of calling token generation method in interceptor in case of token is null like :
if(token == null){
//call token generation fetchToken method
}
It is the right approach
Spring default scope is always singleton if not specified
It is ok to use interceptor, but what if you want to call a API without a token?
Best approach to use two separate methods to send request with token and without token using a separate class
#Component
public class RestClient {
#Autowired
RestTemplate restTemplate;
public HttpHeaders getRequestHeaderBearer() {
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
headers.add(HeaderParameters.AUTHORIZATION, HeaderParameters.BEARER +
TokenGeneration.token);
return headers;
}
public HttpHeaders getRequestHeader() {
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
return headers;
}
public <T> ResponseEntity<T> restExchangeBearer(String url, HttpMethod httpMethod,
Class<T> classObj) {
return restTemplate.exchange(url, httpMethod,
new HttpEntity<>("parameters", this.getRequestHeaderBearer()), classObj);
}
public <T> ResponseEntity<T> restExchange(String url, HttpMethod httpMethod,
Class<T> classObj) {
return restTemplate.exchange(url, httpMethod,
new HttpEntity<>("parameters", this.getRequestHeader()), classObj);
}
}

Use Token in RestTemplate

I want to use this RestTemplate code to make POST requests.
#Bean(name = "simpleRestTemplate")
public RestTemplate getRestClient() {
RestTemplate restClient = new RestTemplate(getClientHttpRequestFactory());
restClient.getInterceptors().add(new BasicAuthorizationInterceptor("username", "password"));
HttpEntity<PaymentTransaction> request = new HttpEntity<>(new PaymentTransaction());
ResponseEntity<PaymentTransaction> response = restClient.exchange("http://example.com", HttpMethod.POST,
request, PaymentTransaction.class);
PaymentTransaction foo = response.getBody();
return restClient;
}
How I can add Toke authentication into the HTTP link?
Probably the easiest way is to use exchange("http://example.com" + "/" + token, HttpMethod.POST,
Is there any better way?
Check out UriComponentsBuilder:
URI uri = UriComponentsBuilder.fromUriString("http://example.com")
.pathSegment(token)
.build()
.toUri();
Then you can use exchange() that takes a URI as its first parameter.
restClient.exchange(uri, HttpMethod.POST, request, PaymentTransaction.class);
As #nickb commented, authentication is best done in HTTP headers.
If you really need to inject a token in the URL, you can implement a custom interceptor.
Pseudo code:
final String tokenValue = "something";
restClient.getInterceptors().add(new ClientHttpRequestInterceptor() {
#Override
ClientHttpResponse intercept(HttpRequest request,
byte[] body,
ClientHttpRequestExecution execution)
throws java.io.IOException {
URI modifiedUri = UriComponentsBuilder.fromUri(request.getURI())
.query("token={tokenPlaceholder}")
.buildAndExpand(tokenValue)
.toUri();
request.setURI(modifiedUri);
}
});
There are many reasons for not doing that, for instance:
Systems that intercept and logs URL would log the token too, allowing 3rd parties to impersonate your users
You need to parse the token from the URL while dealing the rest of the query in the POST body request
https://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/http/client/ClientHttpRequestInterceptor.html

Categories