I would like to retrieve a jwt token, using Spring RestTemplate, from a callback that is a redirect URL from a login URL.
I have been able to connect to the login URL, I have been able to follow the redirect link to the callback, but I have not been able to retieve the token that is stored in the reponse header of the callback.
RestTemplate restTemplate = new RestTemplate();
String url = my_login_url;
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
MultiValueMap<String, String> map = new LinkedMultiValueMap<>();
map.add(my_login);
map.add(my_password);
HttpEntity<MultiValueMap<String, String>> request = new HttpEntity<>(map, headers);
final HttpComponentsClientHttpRequestFactory factory = new HttpComponentsClientHttpRequestFactory();
final HttpClient httpClient = HttpClientBuilder.create()
.setRedirectStrategy(new LaxRedirectStrategy())
.build();
factory.setHttpClient(httpClient);
restTemplate.setRequestFactory(factory);
ResponseEntity<String> response = restTemplate.exchange(url,
HttpMethod.POST,
request,
String.class);
// OUCH !! response does not contain the token I'm trying to retrieve !! The token is in the header of the callback !!
Could anybody help me understand how to access the header of the callback ?
You can use
response.getHeaders().get(HttpHeaders.SET_COOKIE);
(While the client sends the cookies it has via the "Cookie" header, the server sends cookies to set with a "Set-Cookie" header.)
This returns a list because each cookie will be in an extra header with the same name.
After some research, I was able to find a way to retrieve that token, that seems more like a hack to me than an actual solution.
The idea is that the apache HttpClient contains a cookie store with just the token I need. Unfortunately, HttpClient does not have any getters, only execute methods, and I'm already on RestTemplates.
However, it is possible to pass a Cookie Store as a parameter while building the HttpClient instance. That Cookie Store will be filled while posting the authentication request.
The final version of the code looks like that:
public void getCookies() {
RestTemplate restTemplate = new RestTemplate();
String url = my_login_url;
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
MultiValueMap<String, String> map = new LinkedMultiValueMap<>();
map.add("username_key", my_user);
map.add("password_key", my_password);
HttpEntity<MultiValueMap<String, String>> request = new HttpEntity<>(map, headers);
final HttpComponentsClientHttpRequestFactory factory = new
HttpComponentsClientHttpRequestFactory();
CookieStore basicCookieStore = new BasicCookieStore();
final HttpClient httpClient = HttpClientBuilder.create()
.setRedirectStrategy(new LaxRedirectStrategy())
.setDefaultCookieStore(basicCookieStore)
.build();
factory.setHttpClient(httpClient);
restTemplate.setRequestFactory(factory);
restTemplate.exchange(url,
HttpMethod.POST,
request,
String.class);
Cookie cookie = basicCookieStore.getCookies().stream()
.findFirst()
.orElseThrow(() -> new RuntimeException(url));
System.out.println(cookie.getValue());
}
Notice that I am not even interested in the response of the restTemplate.exchange query.
Related
I am basically trying to send a GET request to an api, that only accepts Https connection with the access token. I tried sending to http and I get “Page has moved 400 error” . Is there a way to achieve such thing? Thank you in advance
Here is OkHttp code generated by PostMan if you want to send a http request with headers
OkHttpClient client = new OkHttpClient().newBuilder().build();
Request request = new Request.Builder()
.url("http://localhost:8080/test")
.method("GET", null)
.addHeader("Authorization", "Bearer XXXXXX")
.build();
Response response = client.newCall(request).execute();
If you are using Spring, here is RestTemplate code
RestTemplate template = new RestTemplate();
HttpHeaders headers = new HttpHeaders();
headers.add("Auth","Bear XXXXXX");
HttpEntity<String> httpEntity = new HttpEntity<>(headers);
ResponseEntity<String> result = template.exchange("http://localhost:8080/test", HttpMethod.GET,httpEntity,String.class);
I'm new to Spring and trying to do a rest request with RestTemplate. The Java code should do the same as below curl command:
curl --data "name=feature&color=#5843AD" --header "PRIVATE-TOKEN: xyz" "https://someserver.com/api/v3/projects/1/labels"
But the server rejects the RestTemplate with a 400 Bad Request
RestTemplate restTemplate = new RestTemplate();
HttpHeaders headers = new HttpHeaders();
headers.add("PRIVATE-TOKEN", "xyz");
HttpEntity<String> entity = new HttpEntity<String>("name=feature&color=#5843AD", headers);
ResponseEntity<LabelCreationResponse> response = restTemplate.exchange("https://someserver.com/api/v3/projects/1/labels", HttpMethod.POST, entity, LabelCreationResponse.class);
Can somebody tell me what I'm doing wrong?
I think the problem is that when you try to send data to server didn't set the content type header which should be one of the two: "application/json" or "application/x-www-form-urlencoded" . In your case is: "application/x-www-form-urlencoded" based on your sample params (name and color). This header means "what type of data my client sends to server".
RestTemplate restTemplate = new RestTemplate();
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
headers.add("PRIVATE-TOKEN", "xyz");
MultiValueMap<String, String> map = new LinkedMultiValueMap<>();
map.add("name","feature");
map.add("color","#5843AD");
HttpEntity<MultiValueMap<String, String>> entity = new HttpEntity<>(map, headers);
ResponseEntity<LabelCreationResponse> response =
restTemplate.exchange("https://foo/api/v3/projects/1/labels",
HttpMethod.POST,
entity,
LabelCreationResponse.class);
You need to set the Content-Type to application/json. Content-Type has to be set in the request. Below is the modified code to set the Content-Type
final String uri = "https://someserver.com/api/v3/projects/1/labels";
String input = "US";
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
headers.add("PRIVATE-TOKEN", "xyz");
HttpEntity<String> request = new HttpEntity<String>(input, headers);
ResponseEntity<LabelCreationResponse> response = restTemplate.postForObject(uri, request, LabelCreationResponse.class);
Here, HttpEntity is constructed with your input i.e "US" and with headers.
Let me know if this works, if not then please share the exception.
Cheers!
It may be a Header issue check if the header is a Valid header, are u referring to "BasicAuth" header?
HttpHeaders headers = new HttpHeaders();
headers.add("Content-Type", MediaType.APPLICATION_FORM_URLENCODED.toString());
headers.add("Accept", MediaType.APPLICATION_JSON.toString()); //Optional in case server sends back JSON data
MultiValueMap<String, String> requestBody = new LinkedMultiValueMap<String, String>();
requestBody.add("name", "feature");
requestBody.add("color", "#5843AD");
HttpEntity formEntity = new HttpEntity<MultiValueMap<String, String>>(requestBody, headers);
ResponseEntity<LabelCreationResponse> response =
restTemplate.exchange("https://example.com/api/request", HttpMethod.POST, formEntity, LabelCreationResponse.class);
my issue, the MessageConverters contains other converters may converts then entity to json (like FastJsonHttpMessageConverter). So i added the FormHttpMessageConverter to ahead and it works well.
<T> JuheResult<T> postForm(final String url, final MultiValueMap<String, Object> body) {
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
HttpEntity<MultiValueMap<String, Object>> requestEntity = new HttpEntity<>(body, headers);
return exchange(url, HttpMethod.POST, requestEntity);
}
<T> JuheResult<T> exchange(final String url, final HttpMethod method, final HttpEntity<?> requestEntity) {
ResponseEntity<JuheResult<T>> response = restTemplate.exchange(url, method, requestEntity,
new JuheResultTypeReference<>());
logger.debug("调用结果 {}", response.getBody());
return response.getBody();
}
public JuheSupplierServiceImpl(RestTemplateBuilder restTemplateBuilder) {
Duration connectTimeout = Duration.ofSeconds(5);
Duration readTimeout = Duration.ofSeconds(5);
restTemplate = restTemplateBuilder.setConnectTimeout(connectTimeout).setReadTimeout(readTimeout)
.additionalInterceptors(interceptor()).build();
restTemplate.getMessageConverters().add(0, new FormHttpMessageConverter());
}
fastjson prevent resttemplate converting other mediaTypes other than json
I'm trying to revoke google token following API ("Content-type:application/x-www-form-urlencoded", request type POST, https://accounts.google.com/o/oauth2/revoke?token={token}).
My code example is:
private static final String REVOKE_URL = "https://accounts.google.com/o/oauth2/revoke";
private static final String TOKEN = "token";
#Autowired
private RestTemplate restTemplate;
public void revokeToken(TokenDetailsDto tokenDetailsDto) {
HttpHeaders headers = new HttpHeaders();
headers.add(CONTENT_TYPE, MediaType.APPLICATION_FORM_URLENCODED_VALUE);
HttpEntity<?> entity = new HttpEntity(headers);
Map<String, String> parameters = new HashMap<>();
parameters.put(TOKEN, tokenDetailsDto.getRefreshToken());
LOGGER.info("used parameters:\n\turl={},\n\tentity={},\n\turiParameters={}", REVOKE_URL, entity, uriParameters);
restTemplate.exchange(REVOKE_URL, HttpMethod.GET, entity, Void.class, parameters);
}
result is exception:
17:30:54,323 INFO # com.nextiva.calendar.client.google.GoogleRevokeAccessService.revokeToken used parameters:
url=https://accounts.google.com/o/oauth2/revoke,
entity=<{Content-Type=[application/x-www-form-urlencoded]}>,
uriParameters={token=1/mzMH8VPrltarCo3LOHU_tBWi4qK5e20uIM0R_Al9T34}
17:31:20,718 ERROR # com.nextiva.calendar.web.error.handling.BaseExceptionHandler.exception 400 Bad Request
org.springframework.web.client.HttpClientErrorException: 400 Bad Request
at org.springframework.web.client.DefaultResponseErrorHandler.handleError(DefaultResponseErrorHandler.java:63)
at org.springframework.web.client.RestTemplate.handleResponse(RestTemplate.java:700)
at org.springframework.web.client.RestTemplate.doExecute(RestTemplate.java:653)
at org.springframework.web.client.RestTemplate.execute(RestTemplate.java:621)
at org.springframework.web.client.RestTemplate.exchange(RestTemplate.java:540)
I try use rest client to handle request like in documentation it is ok (using rest client I tried to use POST request with specified content type).
Same error result if I use
restTemplate.postForEntity(REVOKE_URL, entity, Void.class, uriParameters);
So issue is in restTemplate using (my spring part). Is any ideas, what I've done wrong here?
Solution is:
HttpHeaders headers = new HttpHeaders();
headers.add(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_FORM_URLENCODED_VALUE);
HttpEntity<?> entity = new HttpEntity(headers);
UriComponentsBuilder builder = UriComponentsBuilder.fromHttpUrl(REVOKE_URL)
.queryParam("token", tokenDetailsDto.getRefreshToken());
LOGGER.info("used parameters:\n\turl={},\n\tentity={},\n\turiParameters={}", REVOKE_URL, entity);
restTemplate.postForObject(builder.build().encode().toUri(), entity, Void.class);
Small explanation: service expect urlParameters, but parameters was not urlParameters, its request parameters. So I used UriComponentsBuilder to build correct request.
I have Spring Rest service defined as below.
#RequestMapping(value = "/mobilebuild", method = RequestMethod.POST)
public StringWrapper buildApp(#RequestParam("projectName") String projectName, #RequestParam("appId") String projectId, #RequestParam("version") String version, #RequestParam("app") MultipartFile file) {
//Process to build app
return WMUtils.SUCCESS_RESPONSE;
}
From client side i am using rest template as follows
final List<HttpMessageConverter<?>> messageConverters = new ArrayList<>();
messageConverters.add(new ByteArrayHttpMessageConverter());
messageConverters.add(new StringHttpMessageConverter(Charset.forName(CommonConstants.UTF8)));
messageConverters.add(new ResourceHttpMessageConverter());
messageConverters.add(new SourceHttpMessageConverter<Source>());
messageConverters.add(new AllEncompassingFormHttpMessageConverter());
messageConverters.add(new FormHttpMessageConverter());
RestTemplate template = new RestTemplate(messageConverters);
MultiValueMap<String, Object> parts = new LinkedMultiValueMap<String, Object>();
//Post Parameters
parts.add("projectName", "FirstProject");
parts.add("appId", "app12345");
parts.add("version", "1.0");
// MultipartFile
parts.add("app", new FileSystemResource(tempFilesStorageManager.getFilePath("/tmp/app.zip")));
HttpHeaders headers = new HttpHeaders();
headers.add("Cookie", auth);
headers.setContentType(MediaType.MULTIPART_FORM_DATA);
String url = "http://localhost:8080/AppManager/services/mobilebuild";
HttpEntity<MultiValueMap> requestEntity = new HttpEntity<MultiValueMap>(parts, headers);
ResponseEntity<String> responseEntity = template.postForEntity(endpointAddress, requestEntity, String.class);
String response = responseEntity.getBody();
I am unable to read the request parameters from controller (server): getting the following error
Error: request parameter projectName is not present in the request.
So please suggest me the way to achieve this.
According to javadoc of HttpEntity , the first parameter is request body and second one is request headers, but your client is sending request parameters inside request body and your controller is expecting them as #RequestParam, hence the error.
So either change your client to send the request parameters in the end point address URL to match your server side as ...projectName=FirstProject&appId= app12345&version=1.0....
Or encapsulate all your #RequestParam fields inside a single DTO class and add #RequestBody annotation on server side if your client wants to send in request body.
Can someone please help me figure out what is wrong with the code below?
I am using Spring 3.1.1 RestTemplate to try to call a REST WS on Box.com to get a new access token from a refresh token.
The code below is returning a 400 (BAD REQUEST). I am able to successfully call the same method using the FireFox POST plugin. I've compared output from the writeForm method on the FormHttpMessageConverter class and it is exactly as I am sending it from FireFox.
Does anyone have any ideas?
public static void main(String[] args) throws InterruptedException {
try {
String apiUrl = "https://www.box.com/api/oauth2/token";
String clientSecret = "[MY SECRET]";
String clientId = "[MY ID]";
String currentRefreshToken = "[MY CURRENT VALID REFRESHTOKEN]";
RestTemplate restTemplate = new RestTemplate();
List<HttpMessageConverter<?>> messageConverters = new ArrayList<HttpMessageConverter<?>>();
messageConverters.add(new FormHttpMessageConverter());
restTemplate.setMessageConverters(messageConverters);
MultiValueMap<String, String> body = new LinkedMultiValueMap<String, String>();
body.add("grant_type", "refresh_token");
body.add("refresh_token", currentRefreshToken);
body.add("client_id", clientId);
body.add("client_secret", clientSecret);
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
headers.add("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8,application/json");
headers.add("Accept-Encoding", "gzip, deflate");
HttpEntity<?> entity = new HttpEntity<Object>(body, headers);
restTemplate.exchange(apiUrl, HttpMethod.POST, entity, String.class);
} catch (Exception ex) {
System.out.println("ex = " + ex.getMessage());
}
}
}
The no-arg constructor for RestTemplate uses the java.net API to make requests, which does not support gzip encoding. There is, however, a constructor that accepts a ClientHttpRequestFactory. You can use the HttpComponentsClientHttpRequestFactory implementation, which uses the Apache HttpComponents HttpClient API to make requests. This does support gzip encoding. So you can do something like the following (from the Spring Docs) when creating your RestTemplate:
HttpClient httpClient = HttpClientBuilder.create().build();
ClientHttpRequestFactory requestFactory = new HttpComponentsClientHttpRequestFactory(httpClient);
RestTemplate restTemplate = new RestTemplate(requestFactory);
In Spring Boot, adding something like this to pom.xml seems to add some magic.
<dependency>
<groupId>com.squareup.retrofit2</groupId>
<artifactId>retrofit</artifactId>
<version>2.3.0</version>
</dependency>
I'm assume that there are other, similar, solutions...
double check the HttpHeaders properly !!