Adding Headers to Zuul when re-directing - java

I am trying to use Zuul to redirect calls to a downstream system somewhere else.
In the re-direct, I need to add in a Header with necessary data for the api receiving the redirection to process. I can't seem to get the downstream system to detect this data. Attached is my code.
I am using Zuul from Edgware.SR3, Spring Boot 1.5.12
Zuul Filter
#Component
public class RouteFilter extends ZuulFilter{
#Override
public Object run() {
//Testing to add header
context.getRequest().getParameterMap().put("api", new String[]{"api"});
context.getResponse().setHeader("api", api);
context.addZuulResponseHeader("api", "api");
context.addZuulRequestHeader("api", "api");
context.setSendZuulResponse(false);
context.put(FORWARD_TO_KEY, redirect_urls.get(key));
context.setResponseStatusCode(HttpStatus.SC_TEMPORARY_REDIRECT);
context.getResponse().sendRedirect(redirect_urls.get(key));
return null;
}
}
Redirected Service Code
#RequestMapping(value = "/forward")
public ResponseEntity<String> forwardToMe(#RequestHeader(required = true, name = "api")String api){
return new ResponseEntity<String>("Hi",HttpStatus.OK);
}
Error Received in Postman
{
"timestamp": 1524737817729,
"status": 400,
"error": "Bad Request",
"exception": "org.springframework.web.bind.ServletRequestBindingException",
"message": "Missing request header 'api' for method parameter of type String",
"path": "/forward" }

I guess you use a Route Filter, maybe you can try with a Pre Filter.
Adding a custom header can be done with something like this : context.addZuulRequestHeader("Authorization", "Basic " + credentials);.
For the redirection part, you can check this thread

A little late my response but works fine
As referred in the official documentation Cookies and Sensitive Headers
The sensitiveHeaders are a blacklist, and the default is not empty. Consequently, to make Zuul send all headers (except the ignored ones), you must explicitly set it to the empty list. Doing so is necessary if you want to pass cookie or authorization headers to your back end. The following example shows how to use sensitiveHeaders:
zuul:
routes:
entry:
path: /users/**
strip-prefix: false
service-id: users-service
sensitive-headers:
- Cookie,Set-Cookie
This implemented example can also help you

In case if anyone still facing this issue,
In Zuul Proxy add the header to RequestContext as below:
RequestContext ctx = RequestContext.getCurrentContext();
ctx.addZuulRequestHeader("param", "value");
And then in the respective microservices write a custom filter and extract the value as below
#Component
public class MyFilter extends OncePerRequestFilter {
#Override
protected void doFilterInternal(HttpServletRequest request,
HttpServletResponse response,
FilterChain filterChain)
throws ServletException, IOException {
String headerParam = request.getHeaders("param").nextElement();
logger.info("headerParam: "+headerParam);
filterChain.doFilter(request, response);
}
}

I update my comment here just in case if anyone is still facing this problem.
I found this problem recently and resolved by adding the following configuration in my application.yml
application.yml
...
zuul:
sensitive-headers:
- Cookie,Set-Cookie
...
Reference Link below:
https://cloud.spring.io/spring-cloud-static/Dalston.SR5/multi/multi__router_and_filter_zuul.html

RequestContext ctx = RequestContext.getCurrentContext();
String auth = "useeerrr" + ":" + "passsss";
ctx.addZuulRequestHeader("Authorization", "Basic " +
Base64Variants.MIME_NO_LINEFEEDS.encode(auth.getBytes(StandardCharsets.US_ASCII)));
ctx.addZuulRequestHeader("X-USERNAME-HEADER","xxx");
Map<String, List<String>> newParameterMap = new HashMap<>();
Map<String, String[]> parameterMap = ctx.getRequest().getParameterMap();
for (Map.Entry<String, String[]> entry : parameterMap.entrySet()) {
String key = entry.getKey();
String[] values = entry.getValue();
newParameterMap.put(key, Arrays.asList(values));
}
String authenticatedKey = "authenticated";
String authenticatedValue = "true";
newParameterMap.put(authenticatedKey,Arrays.asList(authenticatedValue));
ctx.setRequestQueryParams(newParameterMap);
HttpServletRequest request = ctx.getRequest();
logger.info(String.format("%s request to %s", request.getMethod(), request.getRequestURL().toString()));
return null;

Related

GraphQL + Springboot + Http Headers

I'm using these graphql dependencies:
"com.graphql-java-kickstart:graphql-spring-boot-starter:12.0.0",
"com.graphql-java-kickstart:graphql-java-tools:12.0.0",
And how can I get httpRequestHeaders from the DataFetchingEnvironment class. I see DataFetchingEnvironment.getContext() is deprecated is there any other alternative?
Current logic:
GraphQLServletContext servletContext = env.getContext(); //deprecated
GraphQLContext qlContext = env.getGraphQlContext(); // No httpRequest
var httpRequest = servletContext.getHttpServletRequest();
You can do:
env.getGraphQlContext().get(HttpServletRequest.class).getHeader("header")
As of 12.0.0, environment.getGraphQLContext() is not supported. This lack of support is referenced in the project's github (https://github.com/graphql-java-kickstart/graphql-spring-boot/issues/808)
As of version 13.0.0, you can access the HttpServletRequest by using
HttpServletRequest httpServletRequest = environment.getGraphQlContext().get(HttpServletRequest.class);
If you need to use version 12.0.0, my suggestion would be to use a OncePerRequestFilter and store the header information you need in a ThreadLocal value. Just remember to clear that thread local value after the request has been processed. This will only work if you turn off "async mode" that the GraphQL library introduced in 11.0.0, as "async mode" processes the request filters and graphql resolvers in different threads.
Upgrade to 13.0.0 (or 14.0.0) and call
HttpServletRequest httpServletRequest = env.getGraphQlContext().get(HttpServletRequest.class);
This will give you and HttpServletRequest, from where you can call
private static Map<String, String> getHeadersFromRequest(HttpServletRequest httpServletRequest) {
var mapOfHeaders = new HashMap<String, String>();
Enumeration<String> headerNames = httpServletRequest.getHeaderNames();
if (headerNames != null) {
while (headerNames.hasMoreElements()) {
String headerName = headerNames.nextElement();
mapOfHeaders.put(headerName, httpServletRequest.getHeader(headerName));
}
}
return mapOfHeaders;
}
to return a list map of headers.

The request body is sent as json even though the content type is set as application/x-www-form-urlencoded

This is related to an existing spring boot question raised by me(Request Body is not properly encoded and hidden when using spring form encoder in Feign Client).
According to this question, we can add either content type in headers or add during request mapping itself as consumes.
So what I did was added content type in headers in the client configuration class
public class EmailClientConfiguration {
#Bean
public RequestInterceptor requestInterceptor(Account<Account> account) {
return template -> {
template.header("Content-Type", "application/x-www-form-urlencoded");
};
}
#Bean
public OkHttpClient client() {
return new OkHttpClient();
}
#Bean
Logger.Level feignLoggerLevel() {
return Logger.Level.FULL;
}
#Bean
public Decoder feignDecoder() {
return new JacksonDecoder();
}
#Bean
public Encoder feignFormEncoder () {
return new SpringFormEncoder(new JacksonEncoder());
}
}
and I see in the headers the content type is correctly set as application/x-www-form-urlencoded when the request is sent. But the request body is still sent in json format and also not hidden.
Request Body:
Map<String, String> requestBody = new HashMap<>();
requestBody.put("username", "xyz");
requestBody.put("email", "xyz#gmail.com");
requestBody.put("key", "xxx");
Request Body received in server end:
{"{\n \"key\" : \"xxx\",\n \"email\" : \"xyz#gmail.com\",\n \"username\" : \"xyz\"\n}"
When I add consumes in my request mapping as application/x-www-form-urlencoded
#FeignClient(name = "email", url = "localhost:3000",
configuration = EmailClientConfiguration.class)
public interface EmailClient {
#PostMapping(value = "/email/send", consumes = "application/x-www-form-urlencoded")
ResponseDto sendEmail(#RequestBody Map<String, String> requestBody);
}
it works fine(request body is hidden in server end and also properly encoded). And when I removed the header in the configuration class and adding only consumes works fine without no issues but the vice versa has this problem.
I searched in internet for this and couldn't find any answer.
Feign encodes the request body and parameters before passing the request to any RequestInterceptor (and rightly so). If you do not declare consumes = "application/x-www-form-urlencoded", SprinFormEncoder doesn't know that you're trying to send form data, so it delegates serialization to the inner JacksonEncoder which only does JSON (see for yourself by printing template.body() before setting the header).
Handling such a well-supported header in the interceptor doesn't seem like a good idea, when you already have consumes. If you insist on doing so, you have to provide your own encoder which doesn't rely on the header value and always outputs form-urlencoded data.

Get request header in spring boot

How do I get the header and body of the current request from an application which called my Springboot application? I need to extract this information. Unfortunately this does not work. I tried to get the current request with this code sample (https://stackoverflow.com/a/26323545/5762515):
public static HttpServletRequest getCurrentHttpRequest(){
RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
if (requestAttributes instanceof ServletRequestAttributes) {
HttpServletRequest request = ((ServletRequestAttributes)requestAttributes).getRequest();
return request;
}
throw new IllegalArgumentException("Request must not be null!");
}
And then I tried to get the body
ContentCachingRequestWrapper requestWrapper = (ContentCachingRequestWrapper) currentRequest;
String requestBody = new String(requestWrapper.getContentAsByteArray());
Can someone tell me what im doing wrong?
Thanks in advance
#RestController
public class SampleController {
#PostMapping("/RestEndpoint")
public ResponseEntity<?> sampleEndpoint(#RequestHeader Map<String, String> headers,#RequestBody Map<String,String> body) {
//Do something with header / body
return null;
}
}
If the application's are communicating through a rest endpoint I believe this would be the simplest solution. In spring you can add RequestHeader and RequestBody annotations to method arguments to have them setup to be used.
Of course you can map RequestBody directly to some POJO instead of using a map but just as an example.
Let me know if this is what you were looking for !
#TryHard, You're using spring boot then following way is more preferable for you,
#RestController
public class SampleController {
#RequestMapping("/get-header-data")
public ResponseEntity<?> sampleEndpoint(HttpServletRequest request) {
// request object comes with various in-built methods use as per your requirement.
request.getHeader("<key>");
}
}
you can get header with your code but need apply some changes.
private String getRequest() throws Exception {
RequestAttributes attribs = RequestContextHolder.getRequestAttributes();
if (attribs != null) {
HttpServletRequest request = ((ServletRequestAttributes) attribs).getRequest();
return request ;
}
throw new IllegalArgumentException("Request must not be null!");
}
after you can extract header info from request. For example if you want get Accept-Encoding
String headerEncoding = getRequest().getHeader("Accept-Encoding");
obliviusly you don't use this approce if not necessary.
If you want exract the body NOT use this solution

How to extract the response body into post filter using zuul

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"));

Jersey 2.x add multiple headers to ClientConfig

I've looked at different answers on here and they're all about adding header(s) during a request call. I would like to add headers in the client config then register it with the client itself.
I've looked around and found that I can create a custom ClientRequestFilter, but looking at the add() method signatures, I don't see any in which I can add multiple headers - they all take like a string as the first argument, then like a list.
For example, I would like to add these headers:
Accept: 'something'
Client-ID: 'another something'
Authorization: 'OAuth more something'
I came up with the code below, but it seems only the first register() method call is actually used. I checked the debugger and all I see is the first Accept header and the User-Agent header added by Jersey.
public OAuth2Authenticator(String header, String value) {
this.header = header;
this.value = value;
}
#Override
public void filter(ClientRequestContext requestContext) throws IOException {
requestContext.getHeaders().add(header, value);
}
...
client = ClientBuilder.newClient(new ClientConfig());
client.register(new OAuth2Authenticator(HttpHeaders.ACCEPT, API_VERSION))
.register(new OAuth2Authenticator("Client-ID", clientId))
.register(new OAuth2Authenticator(HttpHeaders.AUTHORIZATION, "OAuth " + accessToken));
I believe you can do :
client.target("")
.request()
.header("Accept","something")
.header("Client-ID", "another something");
I was able to get it to work by assigning to a MultivalueMap first, then calling add().
MultivaluedMap<String, Object> headers = requestContext.getHeaders();
headers.add(HttpHeaders.ACCEPT, "something");
headers.add("Client-ID", another something);
headers.add(HttpHeaders.AUTHORIZATION, "OAuth more something");
...
client = ClientBuilder.newClient(new ClientConfig());
client.register(new OAuth2Authenticator( API_VERSION, clientId, accessToken));

Categories

Resources