I developped a spring-boot application. When I need to do REST requests (POST, PUT...) I use every time basic authentication like that
HttpEntity<PostJql> httpEntity = new HttpEntity<PostJql>(post, jiraUtils.getHeader());
ResponseEntity<IssueDataWrapper> response = restTemplate.exchange(urlJiraResponse, HttpMethod.POST, httpEntity, IssueDataWrapper.class);
Is there another way to do basic authentication once time in the application and then use RestTemplate or httpPost {Put/Delete...} without basic authentication ?
Best regards
Yes. Configure your RestTemplate with a ClientHttpRequestFactory that does what you need.
You can just set it in the constructor:
restTemplate = new RestTemplate(requestFactory);
... or you can make all of these things beans and let Spring wire it.
You could extend SimpleClientHttpRequestFactory, overriding createRequest():
public class BasicAuthSimpleClientHttpRequestFactory
extends SimpleClientHttpRequestFactory {
#Override
public HttpClientRequest createRequest(URI uri, HttpMethod httpMethod) {
HttpClientRequest request = super.createRequest(uri, httpMethod);
HttpHeaders headers = request.getHeaders();
headers.add(... your auth header ...);
return request;
}
}
... or you could bring in Apache HttpComponents as a dependency and use an HttpComponentsClientHttpRequestFactory, and configure the underlying Apache HttpClient to do the authentication you need. Apache documents this in detail, but to get you started:
BasicCredentialsProvider credentialsProvider =
new BasicCredentialsProvider();
credentialsProvider.setCredentials(AuthScope.ANY,
new UsernamePasswordCredentials("user", "password"));
HttpClient client = HttpClientBuilder.create()
.setDefaultCredentialsProvider(credentialsProvider)
.build();
RestTemplate restTemplate = new RestTemplate(
new HttpComponentsClientHttpRequestFactory(client));
In my experience it's worth bringing in Apache HttpComponents if you can -- it is more reliable and configurable than the HttpUrlConnection based HTTP client that RestTemplate otherwise uses by default. When your requirements broaden to things like other authentication methods, timeouts, retry strategies etc., you'll be glad you have an HttpClient to configure.
Related
I'm using Spring 4 RestTemplate to do some server-side API calls.
The RestTemplate instance is a custom one (not Spring Boot default) using Apache HttpClient created as follows:
PoolingHttpClientConnectionManager cm;
...
CloseableHttpClient httpClient = HttpClients.custom()
.setConnectionManager(cm)
.build();
Some of the API calls use HTTP Basic Authentication and thus need to have an Authorization header. I'm adding this on a RequestEntity and then performing an exchange() call on the RestTemplate and this works fine.
RequestEntity<Void> requestEntity;
requestEntity = RequestEntity.get(uri)
.accept(MediaType.valueOf("application/repository.folder+json"))
.acceptCharset(UTF_8)
.header("Accept-Encoding", "")
.header("Authorization", apiBasicAuthHeader())
.build();
Some other API calls (to the same backend server) should not use HTTP Basic Authentication and instead use a pre-authenticated token supplied as a request parameter.
RequestEntity<Void> requestEntity = RequestEntity.get(uriWithToken)
.accept(APPLICATION_JSON)
.acceptCharset(UTF_8)
.header("Accept-Encoding", "")
.build();
operations.exchange(requestEntity, ResourceLookupResults.class)
This also works fine by itself.
However, if I do an API call using the Authorization header first and then try to do one with the pre-authenticated token (with the same RestTemplate), it seems that the Authorization header is still sent on the 2nd request. I'd expect the header added to the RequestEntity to be added only for that specific request and not for subsequent requests that don't need it. Why is this and what is the best way to avoid this (like using separate RestTemplate instances)?
In Spring Boot 1.5.x, I could use interceptors with AsyncRestTemplate to grab headers from an incoming request to a RestController endpoint and put them in any exchange requests made via the AsyncRestTemplate.
I don't see how this can work with the WebClient. It looks like if you build a WebClient that all its headers, etc are set and unchangeable:
WebClient client = WebClient.builder()
.baseUrl( "http://blah.com" )
.defaultHeader( "Authorization", "Bearer ey..." )
.build();
While I can change these using client.mutate(), that instantiates a completely new WebClient object. I'd prefer not to have to create a new one on every request. Is there no way to keep a WebClient and have per-request headers and other parameters?
It seems like a big waste and poor performance to force creating a new object every time.
What you're using here are the default headers that should be sent for all requests sent by this WebClient instance. So this is useful for general purpose headers.
You can of course change the request headers on a per-request basis like this:
Mono<String> result = this.webClient.get()
.uri("/greeting")
.header("Something", "value")
.retrieve().bodyToMono(String.class);
If you wish to have an interceptor-like mechanism to mutate the request before sending it, you can configure the WebClient instance with a filter:
WebClient
.builder()
.filter((request, next) -> {
// you can mutate the request before sending it
ClientRequest newRequest = ClientRequest.from(request)
.header("Something", "value").build();
return next.exchange(newRequest);
})
Please check out the Spring Framework documentation about WebClient.
I'm using spring RestTemplate.exchange(URI).
The uri was assembled with UriComponentsBuilder.
one of my query params was
query=München
If I take the url generated from the logs, and use it with curl, it works.
When I let rest template run the query, it does not.
Something about the umlaut not getting encoded?
How do I get UriCompenentsBuilder and/or RestTemplate to cope with the umlaut?
I know I can use a string url with RestTemplate that looks like this:
query=M%C3%BCnchen
and that works.
It happens when RestTemplate is initialized with Spring default request factory (SimpleClientHttpRequestFactory) , e.g.:
RestTemplate restTemplate = new RestTemplate();
Replace default request factory with HttpComponentsClientHttpRequestFactory and Apache HttpClient:
CloseableHttpClient httpClient = HttpClientBuilder
.create()
.build();
HttpComponentsClientHttpRequestFactory factory = new HttpComponentsClientHttpRequestFactory(httpClient);
restTemplate = new RestTemplate(factory);
Then call restTemplate.exchange() as you would normally.
I have a SOAP webservice endpoint url(let's say :"EP1") which we can connect only through a proxy server.
We are using org.springframework.ws.client.core.WebServiceTemplate class to consume webservices.
My question is, Is there a way that I can pass/set the proxy server details on WebServiceTemplate to connect to endpoint url "EP1"?
You can specify custom HttpClient via HttpComponentsMessageSender when constructing the WebServiceTemplate. You can then pass default request config which includes the proxy to the client. Something like this should work:
RequestConfig config = RequestConfig
.custom()
.setProxy(new HttpHost("host"))
.build();
CloseableHttpClient client = HttpClients
.custom()
.setDefaultRequestConfig(config)
.build();
HttpComponentsMessageSender messageSender = new HttpComponentsMessageSender(client);
WebServiceTemplate wsTemplate = new WebServiceTemplate();
wsTemplate.setMessageSender(messageSender);
// Set other required properties ...
You might need to set other properties on the WebServiceTemplate object or the HttpClient depending on your needs so on. But this should demonstrate the basic concept.
Also take a look at this sample illustrating the usage of proxies in Apache HTTP client.
You must use VM arguments: -Dhttp.proxyHost=mydomain.com -Dhttp.proxyPort=8080
Having in mind that this setting applies to all HTTP requests made from Java.
RestTemplate restTemplate = new RestTemplate();
final MappingJackson2XmlHttpMessageConverter converter = new MappingJackson2XmlHttpMessageConverter();
final List<MediaType> supportedMediaTypes = new LinkedList<MediaType>(converter.getSupportedMediaTypes());
supportedMediaTypes.add(MediaType.ALL);
converter.setSupportedMediaTypes(supportedMediaTypes);
restTemplate.getMessageConverters().add(converter);
ResponseEntity<MyDTO[]> response = restTemplate.getForEntity(urlBase, MyDTO[].class);
HttpHeaders headers = response.getHeaders();
URI location = headers.getLocation(); // Has my redirect URI
response.getBody(); //Always null
I was under the impression that a 302 would automatically be followed. Am I incorrect in this assumption? I now need to pick off this location and re-request?
Using the default ClientHttpRequestFactory implementation - which is the SimpleClientHttpRequestFactory - the default behaviour is to follow the URL of the location header (for responses with status codes 3xx) - but only if the initial request was a GETrequest.
Details can be found in this class - searching for the following method:
protected void prepareConnection(HttpURLConnection connection, String httpMethod) throws IOException {
...
if ("GET".equals(httpMethod)) {
connection.setInstanceFollowRedirects(true);
}
Here the relevant doc comment of HttpURLConnection.setInstanceFollowRedirects method:
Sets whether HTTP redirects (requests with response code 3xx) should
be automatically followed by this {#code HttpURLConnection}
instance.
The default value comes from followRedirects, which defaults to true.
Are you trying to redirect from one protocol to another, e.g. from http to https or vise versa? If so the automatic redirect won't work. See this comment: URLConnection Doesn't Follow Redirect
After discussion among Java Networking engineers, it is felt that we
shouldn't automatically follow redirect from one protocol to another,
for instance, from http to https and vise versa, doing so may have
serious security consequences
from https://bugs.java.com/bugdatabase/view_bug.do?bug_id=4620571
Otherwise if you debug the RestTemplate code you will see that by default HttpURLConnection is set properly with getInstanceFollowRedirects() == true.
When using the CommonsClientHttpRequestFactory (which uses HttpClient v3 underneath) you can override the postProcessCommonsHttpMethod method and set to follow redirects.
public class FollowRedirectsCommonsClientHttpRequestFactory extends CommonsClientHttpRequestFactory {
#Override
protected void postProcessCommonsHttpMethod(HttpMethodBase httpMethod) {
httpMethod.setFollowRedirects(true);
}
}
You can then use it like this (with optional, possibly preconfigured, HttpClient instance) and requests will follow the location headers in response:
RestTemplate restTemplate = new RestTemplate(
new FollowRedirectsCommonsClientHttpRequestFactory());
Try create RestTemplate like this (Spring config):
#Bean("smartRestTemplate")
public RestTemplate restTemplate(RestTemplateBuilder restTemplateBuilder) {
return restTemplateBuilder
.setConnectTimeout(Duration.ofSeconds(..))
.setReadTimeout(Duration.ofSeconds(..))
.build();
}