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.
Related
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.
I'm using Spring's (version 2.5.4) RESTTemplate to make a POST call to a REST end point (url). Here is the code I have:
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
MultiValueMap<String,String> requestBody = new LinkedMultiValueMap<>();
requestBody.add("token", "abc");
HttpEntity<MultiValueMap<String,String>> request = new HttpEntity<>(requestBody, headers);
try
{
ResponseEntity<TokenValidationResponse> responseEntity = restTemplate.exchange(url, HttpMethod.POST, request, TokenValidationResponse.class);
TokenValidationResponse response = responseEntity.getBody();
Boolean isActive = response.getActive();
if(isActive == null || !isActive) {
log.info("The token supplied isn't active");
}
}
catch(RestClientException e)
{
log.error("An error occurred while posting query to security URI for token validation", e);
}
I keep running into a 400 bad request -
org.springframework.web.client.HttpClientErrorException$BadRequest: 400 Bad Request: [{"error_description":"token parameter is required for the security endpoint.","error":"invalid_request"}]
at org.springframework.web.client.HttpClientErrorException.create(HttpClientErrorException.java:101)
at org.springframework.web.client.DefaultResponseErrorHandler.handleError(DefaultResponseErrorHandler.java:186)
at org.springframework.web.client.DefaultResponseErrorHandler.handleError(DefaultResponseErrorHandler.java:125)
at org.springframework.web.client.ResponseErrorHandler.handleError(ResponseErrorHandler.java:63)
at org.springframework.web.client.RestTemplate.handleResponse(RestTemplate.java:819)
at org.springframework.web.client.RestTemplate.doExecute(RestTemplate.java:777)
at org.springframework.web.client.RestTemplate.execute(RestTemplate.java:711)
at org.springframework.web.client.RestTemplate.exchange(RestTemplate.java:602)
This is the requirement given for the security endpoint url - This endpoint accepts only the HTTP POST method. The required Content-Type value is application/x-www-form-urlencoded. The endpoint has the following parameters: token
I can see the token being passed in though. I am able to verify that the call works fine on POSTMAN when I supply the token as part of the body. I was wondering if there is something I am missing?
You need to specify the token in the headers for example:
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
headers.set("Authorization", "Bearer "+accessToken);
I suggest to see this helpfull post
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.
I need to send a POST HTTP request using Robospice for Android that will have data in the following format:
Content-Type: application/json
{'var1':'var1val','telNumber':'var2val'}
There may be other HTTP headers inside but this is the only header required by the server - I've tested it with a simple HelloWorld request that doesn't require any specific objects in the body.
The problem is that I can't send request body in the right format. I'm doing everything according the Robospice tutorial.
loadDataFromNetwork() method
public T loadDataFromNetwork() throws Exception { // I'm making generic requests, T is a class representing given request
Uri.Builder uriBuilder = Uri.parse(mUrl).buildUpon(); // mURL is just a full URL, including the method (just HTTP, not HTTPS)
MultiValueMap<String, String> body = new LinkedMultiValueMap<String, String>();
for (NameValuePair nvp : mPostParams) { // From the constructor: mPostParams = new ArrayList<NameValuePair>();
body.add(nvp.getName(), nvp.getValue());
}
HttpEntity<?> requestEntity = new HttpEntity<Object>(body, mHttpHeaders);
RestTemplate restTemplate = new RestTemplate();
ResponseEntity<T> responseEntity = getRestTemplate().exchange(
uriBuilder.build().toString(), HttpMethod.POST, requestEntity,
mClazz);
return responseEntity.getBody();
}
createRestTemplate() in the JsonSpiceService class (which extends SpringAndroidSpiceService)
public RestTemplate createRestTemplate() {
RestTemplate restTemplate = new RestTemplate(true);
MappingJacksonHttpMessageConverter jsonConverter = new MappingJacksonHttpMessageConverter();
FormHttpMessageConverter formHttpMessageConverter = new
FormHttpMessageConverter();
StringHttpMessageConverter stringHttpMessageConverter = new StringHttpMessageConverter();
final List<HttpMessageConverter<?>> listHttpMessageConverters = restTemplate
.getMessageConverters();
setTimeout(restTemplate);
listHttpMessageConverters.add(jsonConverter);
listHttpMessageConverters.add(formHttpMessageConverter);
listHttpMessageConverters.add(stringHttpMessageConverter);
restTemplate.setMessageConverters(listHttpMessageConverters);
return restTemplate;
}
Doing it this way, I keep getting 500 Internal Server Error and I am pretty sure it is related to format of the body. Is there any way of getting a raw HTTP request (not the toString() version of some methods just a raw request)? What else can I be doing wrong - can it be about encoding of the body?
Note that I was testing the very same request with both Chrome app for sending REST requests and curl (worked fine).
You are getting confused in your SpringAndroid usage. Look at the docs of HttpEntity, you will see that you use a MultiValueMap as the Body. In that case, you are right, body.toString is called.
MultiValueMap is used in the way you think it is only for headers. For the body, HttpEntity expects a Pojo basically, and thus considers you MultiValueMap as a Pojo.
A simple workaround would be to create by hand the string you want to post as a body :
String body = "key=value&key=value&key=value"
This argument could be used as the first parameter of the constructor of HttpEntity.
I'm using Google Translate API with Spring RestTemplate in my application and it works fine until I use GET http request. However if I heve large piece of data the service will return 414 error (Request-URI Too Large).
So I decided to use POST (according to the note).
And here is my code:
String content = "q=Hello";
HttpHeaders headers = new HttpHeaders();
headers.set("X-HTTP-Method-Override", "GET");
headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
HttpEntity<String> request = new HttpEntity<String>(content, headers);
String response = new RestTemplate.postForObject(
"https://www.googleapis.com/language/translate/" +
"v2?key=<my_key>&source=en&target=ru", request, String.class);
The service returns error 400 bad request and I have no idea why. I have successfully created similar code using jQuery so I know this way should work.
Please help me to fix the problem.
I think the way you form URI is incorrect and thats why you get 400. Looking at the google translate API documentation, it looks like they expect 'q' as URI query parameter. Also you seem to be doing a POST request for a GET. From google translate API docs
GET https://www.googleapis.com/language/translate/v2?key=INSERT-YOUR-KEY&source=en&target=de&q=Hello%20world
Try this,
Map<String, String> queryParameters = new HashMap<String, String>();
queryParameters.put("key","my_key_here");
queryParameters.put("source","en");
queryParameters.put("target","ru");
queryParameters.put("q","Hello World");
String url = "https://www.googleapis.com/language/translate/" +
"v2?key={key}&source={source}&target={target}&q={q}";
HttpHeaders headers = new HttpHeaders();
headers.set("X-HTTP-Method-Override", "GET");
headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
HttpEntity<String> request = new HttpEntity<String>(null, headers);
RestTemplate restclient = new RestTemplate();
String response=restclient.getForObject(url,request,String.class,queryParameters);