I need to apply an Authorization header in a request interceptor, but I need to sign the request method, URI, and date.
Inside the request interceptor I get a RequestInterceptor.RequestFacade, which only has "setter methods"
Is there any way I can get request properties inside a request interceptor?
Ah, did some more googling. The way to do this is to use a client wrapper. Observe...
public class SigningClient implements Client {
final Client wrapped;
public SigningClient(Client client) {
wrapped = client;
}
#Override public Response execute(Request request) {
Request newRequest = sign(request);
return wrapped.execute(newRequest);
}
private void sign(Request request) {
// magic
}
}
Found it here: https://github.com/square/retrofit/issues/185#issuecomment-17819547
Related
My requirement is to receive an encrypted request of content-type text/plain. Using the zuul pre filter I will have to decrypt the request and forward the decrypted request to the underlying services.
But all the services are expecting a request of content-type application/json. Because of which it is returning a 415 error.
I had tried modifying the content type using the below method, but still its giving a 415 error.
public void modifyRequestBody(String requestBody, RequestContext context) {
.....
//code to decrypt request body
.....
HttpServletRequest request = context.getRequest();
MutableHttpServletRequest mutableRequest = new MutableHttpServletRequest(request);
mutableRequest.putHeader("Content-Type", APPLICATION_JSON_WITH_UTF8_CHARSET);
byte[] bytes = StringUtils.isBlank(plainText) ? requestBody.getBytes(StandardCharsets.UTF_8)
: plainText.getBytes(StandardCharsets.UTF_8);
context.setRequest(new HttpServletRequestWrapper(mutableRequest) {
#Override
public ServletInputStream getInputStream() throws IOException {
return new ServletInputStreamWrapper(bytes);
}
#Override
public int getContentLength() {
return bytes.length;
}
#Override
public long getContentLengthLong() {
return bytes.length;
}
#Override
public String getContentType() {
return APPLICATION_JSON_WITH_UTF8_CHARSET;
}
});
}
The MuttableHttpServletRequest class was implemented based on the approach mentioned here : link
Not sure if something else needs to be changed. Any help would be appreciated.
Thanks!
I would like to AWS sign my HTTP request fired by reactive WebClient of Spring. To sign the request I need access to the followings: URL, HTTP method, query parameters, headers and request body bytes.
I started with writing an ExchangeFilterFunction. Due to ClientRequest interface I can access everything there I need, except the request body:
#Component
public class AwsSigningInterceptor implements ExchangeFilterFunction
{
private final AwsHeaderSigner awsHeaderSigner;
public AwsSigningInterceptor(AwsHeaderSigner awsHeaderSigner)
{
this.awsHeaderSigner = awsHeaderSigner;
}
#Override
public Mono<ClientResponse> filter(ClientRequest request, ExchangeFunction next)
{
Map<String, List<String>> signingHeaders = awsHeaderSigner.createSigningHeaders(request, new byte[]{}, "es", "us-west-2"); // should pass request body bytes in place of new byte[]{}
ClientRequest.Builder requestBuilder = ClientRequest.from(request);
signingHeaders.forEach((key, value) -> requestBuilder.header(key, value.toArray(new String[0])));
return next.exchange(requestBuilder.build());
}
}
In older spring versions we used RestTemplate with a ClientHttpRequestInterceptor. In that case the bytes of the body were exposed, so signing was possible.
As I see in case of WebClient Spring handles the body as a Publisher, so I'm not sure if an ExchangeFilterFunction is a good place to start.
How should I sign the HTTP request?
I'm new to Camel and am trying to get a response from a Netty4 route using a POST request. I'd like to send a JSON and return a string extracted from the body.
My rest setup is as follows:
public class Server extends RouteBuilder {
#Override
public void configure() {
String listenAddress = "0.0.0.0";
int listenPort = 8080;
restConfiguration()
.component("netty4-http")
.scheme("http")
.host(listenAddress)
.dataFormatProperty("prettyPrint", "true")
.bindingMode(RestBindingMode.auto)
.port(listenPort);
rest("/")
.post()
.consumes("application/json; charset=UTF-8")
.to("direct:post");
}
}
Within my Camel route I'd like to send the message back using:
#Component
public class RestRoute extends RouteBuilder {
#Autowired
CamelContext context;
#Override
public void configure() {
from("direct:post")
.log("New Request")
.streamCaching()
.setHeader(Exchange.HTTP_METHOD,constant(org.apache.camel.component.http4.HttpMethods.POST))
.setBody().jsonpath("$.Text") // extract text from JSON
.to("http4://0.0.0.0:8080?bridgeEndpoint=true");
However I get the following error: org.apache.camel.http.common.HttpOperationFailedException: HTTP operation failed invoking http://0.0.0.0:8080 with statusCode: 500
I'd appreciate some help!
Oh you should not send the message back, this happens automatic when the routing ends, then the message at that point is used as the response message for the rest.
So remove
.to("http4://0.0.0.0:8080?bridgeEndpoint=true");
I'm working on a POC i need to use zuul as a sever to route 2 routes first will run normally but it has a custom post filter which will send another request to other api using some data of the response of the first requet,
so need to extract the response body of the first request into my custom post filter and get some specific attributes but i can not find the response as it always be null but the status code is 200.
how can i wait and get a value of specific attribute from the response and get the actual status code not just 200 as default value.
i tried to make this implementation using cloud gateway but i reached the same point of disability of extracting the response.
also i tried to make a response decorator but it failed too.
#Component
public class AddResponseHeaderFilter extends ZuulFilter {
#Override
public String filterType() {
return "post";
}
#Override
public int filterOrder() {
return 1;
}
#Override
public boolean shouldFilter() {
return true;
}
#Override
public Object run() {
System.out.println("this is my filter");
RequestContext context = RequestContext.getCurrentContext();
HttpServletRequest request = new HttpServletRequestWrapper(context.getRequest());
System.out.println(String.format("%s request to %s", request.getMethod(), request.getRequestURL().toString()));
HttpServletResponse servletResponse = context.getResponse();
// return an address only
System.out.println(context.getResponseBody().toString());
servletResponse.addHeader("X-Foo", UUID.randomUUID().toString());
return null;
}
}
RequestContext.getCurrentContext().getResponseDataStream() works fine for me, I am also able to manipulate the response.
import java.nio.charset.Charset;
import org.springframework.util.StreamUtils;
RequestContext ctx = RequestContext.getCurrentContext();
HttpServletRequest request = ctx.getRequest();
String requestLog = StreamUtils.copyToString(request.getInputStream(),
Charset.forName("UTF-8"));
I've been debugging this for three hours, I still cannot explain why my custom headers (registered via a client request filter) are not sent.
The client is configured as such (full source here):
private WebTarget webTarget(String host, String appId, String appKey) {
return newClient(clientConfiguration(appId, appKey))
.target(host + "/rest");
}
private Configuration clientConfiguration(String appId, String appKey) {
ClientConfig config = new ClientConfig();
config.register(requestFilter(appId, appKey));
return config;
}
private ClientRequestFilter requestFilter(String appId, String appKey) {
return new VidalRequestFilter(apiCredentials(appId, appKey));
}
The filter is as follows:
public class VidalRequestFilter implements ClientRequestFilter {
private final ApiCredentials credentials;
public VidalRequestFilter(ApiCredentials credentials) {
this.credentials = credentials;
}
#Override
public void filter(ClientRequestContext requestContext) throws IOException {
MultivaluedMap<String, Object> headers = requestContext.getHeaders();
headers.add(ACCEPT, APPLICATION_ATOM_XML_TYPE);
headers.add("app_id", credentials.getApplicationId());
headers.add("app_key", credentials.getApplicationKey());
}
}
And the call is like:
String response = webTarget
.path("api/packages")
.request()
.get()
.readEntity(String.class);
All I get is 403 forbidden, because the specific endpoint I am calling is protected (the auth is performed with the custom headers defined above).
The weirdest thing is that, while I'm debugging, I see that sun.net.www.MessageHeader is properly invoked during the request write (i.e. the instance is valued as such: sun.net.www.MessageHeader#14f9390f7 pairs: {GET /rest/api/packages HTTP/1.1: null}{Accept: application/atom+xml}{app_id: XXX}{app_key: YYY}{User-Agent: Jersey/2.22.1 (HttpUrlConnection 1.8.0_45)}{Host: ZZZ}{Connection: keep-alive}.
However, I have the confirmation that neither our API server, nor its reverse proxy received GET requests with the required auth headers (a first HEAD request seems to be OK, though).
I know for sure the credentials are good 'cause the equivalent curl command just works!
I tried the straightforward approach to set headers directly when defining the call without any success.
What am I missing?