Spring boot: Add request parameter in interceptor - spring

I need to add a parameter when a request is made:
Currently, I'm adding headers using interceptors. However, I'm running against how to add a parameter to the current request:
#Override
public ClientHttpResponse intercept(
HttpRequest request,
byte[] body,
ClientHttpRequestExecution execution
) throws IOException {
//Adding headers
request
.getHeaders()
.set(
HttpHeaders.AUTHORIZATION,
this.jwtService.getBearer()
);
//HOW TO ADD REQUEST PARAMETER HERE???
return execution.execute(request, body);
}
Any ideas?

Related

Spring - Saml2 - Customize the request built by Saml2WebSsoAuthenticationRequestFilter

The actual implementation of the filter Saml2WebSsoAuthenticationRequestFilter (which responds to saml2/authentication/{regitstrationId}) has this method to create the redirect URL:
private void sendRedirect(HttpServletRequest request, HttpServletResponse response, Saml2RedirectAuthenticationRequest authenticationRequest) throws IOException {
this.authenticationRequestRepository.saveAuthenticationRequest(authenticationRequest, request, response);
UriComponentsBuilder uriBuilder = UriComponentsBuilder.fromUriString(authenticationRequest.getAuthenticationRequestUri());
this.addParameter("SAMLRequest", authenticationRequest.getSamlRequest(), uriBuilder);
this.addParameter("RelayState", authenticationRequest.getRelayState(), uriBuilder);
this.addParameter("SigAlg", authenticationRequest.getSigAlg(), uriBuilder);
this.addParameter("Signature", authenticationRequest.getSignature(), uriBuilder);
String redirectUrl = uriBuilder.build(true).toUriString();
response.sendRedirect(redirectUrl);
}
I need to add another parameter to this request so that the request built by the filter contains (beside SAMLRequest, RelayState, SigAlg and Signature) another query param (in my case it will be a "FIDP", a paramater to preselect a federate IdP)
Thanks!

How to reject the request and send custom message if extra params present in Spring boot REST Api

#PostMapping()
public ResponseEntity<ApiResponse> createContact(
#RequestBody ContactRequest contactRequest) throws IOException {
}
How to reject the API request, if extra params present in the request, by default spring boot ignoring extra parameters.
I believe you can add an HttpServletRequest as a parameter to the controller method (createContact in this case). Then you'll get access to all the parameters that come with the requests (query params, headers, etc.):
#PostMapping
public ResponseEntity<ApiResponse> createContact(HttpServletRequest request,
#RequestBody ContactRequest contactRequest) throws IOException {
boolean isValid = ...// validate for extra parameters
if(!isValid) {
// "reject the request" as you call it...
}
}
First add an additional parameter to the method. This gives you access to information about the request. If Spring sees this parameter then it provides it.
#PostMapping()
public ResponseEntity<ApiResponse> createContact(
#RequestBody ContactRequest contactRequest,
WebRequest webRequest) throws IOException {
if (reportUnknownParameters(webRequest) {
return new ResponseEntity<>(HttpStatus.BAD_REQUEST);
}
}
I do something like this to get the bad request into the log.
private boolean reportUnknownParameters(WebRequest webRequest) {
LongAdder unknownCount = new LongAdder();
webRequest.getParameterMap().keySet().stream()
.filter(key -> !knownParameters.contains(key))
.forEach(key -> {
unknownCount.increment();
log.trace("unknown request parameter \"{}\"=\"{}\"", key, webRequest.getParameter(key));});
return unknownCount.longValue() > 0;
}
add #RequestParam annotation in your methods parameter list and add it as a map, then you can access for it's key list and check if it contains anything else other than your required params.
public ResponseEntity<ApiResponse> createContact(#RequestParam Map<String,String> requestParams, #RequestBody ContactRequest contactRequest) throws IOException {
//Check for requestParams maps keyList and then pass accordingly.
}

Spring RestController handle Get Request with Body values

i currently develop an experimental prototype an wanted to ask if there is a way to accept a response body in a get request.
#RequestMapping(method=RequestMethod.GET, path="/stair/shippingorders", produces=MediaType.APPLICATION_JSON_UTF8_VALUE)
public ResponseEntity<?> getShippingOrder(#RequestBody JsonNode request) throws JsonProcessingException, IOException{
log.info("get body: " + request);
// do stuff
return ResponseEntity.ok(response);
}
the test should looks something like this
#Test
public void shouldAcceptRequestBodyinGetRequest() {
JSONObject body = new JSONObject();
body.appendField("stuff", "{}");
HttpEntity<JSONObject> entity = new HttpEntity<JSONObject>(body);
ResponseEntity<String> result = restTemplate.exchange(GET_URL,HttpMethod.GET, entity, String.class );
assertNotNull(result);
}
GET method doesn't support body, hence it won't be possible to send body as part of request. The common practice is to use POST instead

Why is my RestTemplate ClientHttpRequestInterceptor not called?

I want to use interceptor to add authorization header to every request made via rest template. I am doing it like this:
public FirebaseCloudMessagingRestTemplate(#Autowired RestTemplateBuilder builder, #Value("fcm.server-key") String serverKey) {
builder.additionalInterceptors(new ClientHttpRequestInterceptor() {
#Override
public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution) throws IOException {
request.getHeaders().add("Authorization", "key=" + serverKey);
System.out.println(request.getHeaders());
return execution.execute(request, body);
}
});
this.restTemplate = builder.build();
}
However when I do this
DownstreamHttpMessageResponse response = restTemplate.postForObject(SEND_ENDPOINT, request, DownstreamHttpMessageResponse.class);
Interceptor is not called (Iv put breakpoint in it and it did not fire). Request is made and obvious missing auth key response is returned. Why is my interceptor not called?
Ok I know whats happening. After checking build() implementation I discovered that RestTemplateBuilder is not changing self state when calling additionalInterceptors but returns a new builder with given interceptors. Chaining calls solves the issue.
public FirebaseCloudMessagingRestTemplate(final #Autowired RestTemplateBuilder builder, final #Value("${fcm.server-key}") String serverKey) {
this.restTemplate = builder.additionalInterceptors((request, body, execution) -> {
request.getHeaders().add("Authorization", "key=" + serverKey);
log.debug("Adding authorization header");
return execution.execute(request, body);
}).build();
}

Why OAuth2AccessTokenSupport always send POST request ??

I'm working with a Spring Boot + Spring Security OAuth2 to consume the Restful Oauth2 service.
Our Oauth2 service is always expects HTTP GET But OAuth2AccessTokenSupport always sending HTTP POST.
Result:
resulted in 405 (Method Not Allowed); invoking error handler
protected OAuth2AccessToken retrieveToken(AccessTokenRequest request, OAuth2ProtectedResourceDetails resource,
MultiValueMap<String, String> form, HttpHeaders headers) throws OAuth2AccessDeniedException {
try {
this.authenticationHandler.authenticateTokenRequest(resource, form, headers);
this.tokenRequestEnhancer.enhance(request, resource, form, headers);
AccessTokenRequest copy = request;
ResponseExtractor delegate = getResponseExtractor();
ResponseExtractor extractor = new ResponseExtractor(copy, delegate) {
public OAuth2AccessToken extractData(ClientHttpResponse response) throws IOException {
if (response.getHeaders().containsKey("Set-Cookie")) {
this.val$copy.setCookie(response.getHeaders().getFirst("Set-Cookie"));
}
return ((OAuth2AccessToken) this.val$delegate.extractData(response));
}
};
return ((OAuth2AccessToken) getRestTemplate().execute(getAccessTokenUri(resource, form), getHttpMethod(),
getRequestCallback(resource, form, headers), extractor, form.toSingleValueMap()));
} catch (OAuth2Exception oe) {
throw new OAuth2AccessDeniedException("Access token denied.", resource, oe);
} catch (RestClientException rce) {
throw new OAuth2AccessDeniedException("Error requesting access token.", resource, rce);
}
}
<b>protected HttpMethod getHttpMethod() {
return HttpMethod.POST;
}</b>
protected String getAccessTokenUri(OAuth2ProtectedResourceDetails resource, MultiValueMap<String, String> form) {
String accessTokenUri = resource.getAccessTokenUri();
if (this.logger.isDebugEnabled()) {
this.logger.debug(new StringBuilder().append("Retrieving token from ").append(accessTokenUri).toString());
}
StringBuilder builder = new StringBuilder(accessTokenUri);
String separator;
if (getHttpMethod() == HttpMethod.GET) {
separator = "?";
if (accessTokenUri.contains("?")) {
separator = "&";
}
for (String key : form.keySet()) {
builder.append(separator);
builder.append(new StringBuilder().append(key).append("={").append(key).append("}").toString());
separator = "&";
}
}
return builder.toString();
}
Can Anyone explain me why OAuth2AccessTokenSupport always returns POST and
How to send HTTP GET request
To enable GET requests for the token endpoint, you need to add the following in your AuthorizationServerConfigurerAdapter:
#Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
endpoints.allowedTokenEndpointRequestMethods(HttpMethod.GET, HttpMethod.POST);
}
As for why only POST by default: I think that is due to GET requests potentially sending username and password information as request params (this is certainly the case for password grant). These may well be visible in web server logs, while POST body data is not.
Indeed the RFC for OAuth2 declares that the client must use HTTP POST when requesting an access token (https://www.rfc-editor.org/rfc/rfc6749#section-3.2)

Resources