How to access previous SOAP headers in SoapInterceptor? - spring

Here's my configuration:
A request is captured by my REST controller, I send data via SOAP to my webservice. Then I access some data sending SOAP request to another service and I return gathered data to the user sending the request.
Before sending SOAP request to external webservice I need to set some headers, so I have an interceptor that extends AbstractSoapInterceptor with Phase.PRE_PROTOCOL in constructor by webservice side.
Inside handleMessage() I create new headers and add to SoapMessage but... the data I need to set is inside REST request inside it's headers. So in order to get them I need to have access to HttpServletRequest and then I just get the header using HttpServletRequest#getHeader("header_name").
I've saw here I could just use #Context annotation but it's not available in my Spring (3.0.5) or maybe RESTEasy is something else and that question isn't connected with mine in anyway
I've tried this:
HttpServletRequest request = (HttpServletRequest) message.get(AbstractHTTPDestination.HTTP_REQUEST);
and
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
HttpServletRequest request = attributes.getRequest();
but it's been a shot in the dark and it failed.
Edit:
Thanks to Abel ANEIROS I've added:
<listener-class>org.springframework.web.context.request.RequestContextListener</listener-class>
and was able to get HttpServletRequest from RequestContextHolder.getRequestAttributes() but apperently it's origin is from my webservice and not REST
Edit2:
The structure is different than I thought.
It's: Rest Controller -> MyWebService (SOAP) -> ExternalService (SOAP)
I've created another interceptor in controller side, added headers to SOAP message and now I'm trying to get the headers again in MyWebService side.

Related

GraphQL Subscriptions - Spring Boot Websocket Authentication

We are using the Netflix DGS framework to build our backend to provide a GraphQL API.
In addition to that we use Keykloak as an identity provider which comes with a handy Spring module to add support for authentication and authorization out of the box.
Every request contains a JWT token, which gets validated and from there a SecurityContext object is being generated which is then available in every endpoint.
This is working great for HTTP requests. GraphQL queries and mutations are sent via HTTP, therefore no problem here.
Subscriptions on the other hand use the web socket protocol. A WS request does not contain additional headers, therefore no JWT token is sent with the request.
We can add the token via a payload, the question is now how to set up a Spring Security Filter which creates a Security Context out of the payload.
I guess this is rather Spring specific, basically a filter which intercepts any web socket request (ws://... or wss://...) is needed.
Any help or hint is very much appreciated!
The only way to use headers in web socket messages is in the connection_init message. the headers will be sent by the client in the payload of the message.
The solution I propose is done in 2 steps (We will assume that the name of the header element is "token"):
Intercept the connection_init message, then force the insertion of a new element (token) in the subscription request.
Retrieve the element (token) of the header during the interception of the subscription and feed the context.
Concretely, the solution is the implementation of WebSocketGraphQlInterceptor interface
#Configuration
class SubscriptionInterceptor implements WebSocketGraphQlInterceptor {
#Override
public Mono<Object> handleConnectionInitialization(WebSocketSessionInfo sessionInfo, Map<String, Object> connectionInitPayload) {
sessionInfo.getHeaders().add("token", connectionInitPayload.get("token").toString());
return Mono.just(connectionInitPayload);
}
#Override
public Mono<WebGraphQlResponse> intercept(WebGraphQlRequest request, Chain chain) {
List<String> token = request.getHeaders().getOrEmpty("token");
return chain.next(request).contextWrite(context -> context. Put("token", token.isEmpty() ? "" : token.get(0)));
}
}

Feign Client - Dynamic Authorization Header

I have a service that gets http request with an authorization header.
When processing the request, I want to use a Feign Client to query another service. The query to the other service should include the same authorization header.
Currently I use a Filter to extract the authorization header from the incoming request, store the header in a ThreadLocal.
When building the Feign Client I use a RequestInterceptor to read the authorization header from the ThreadLocal and put it into the request to the other service.
This approach is not ideal, because when I start using things like RxJava or Hystrix, threads are changed while processing the request and I have to move the authorization header ThreadLocal from one thread to another.
What are other options to solve this?
One way that I am thinking about is to create a new FeignClient for each request, this way I would no longer need to store the authorization in a thread local. But is this a good idea?
I think I found a solution for my problem. Using RequestContextHolder I can get a reference to the original request (also from spawned child threads) and copy the header from there:
public class AuthForwardInterceptor implements RequestInterceptor {
#Override
public void apply(RequestTemplate template) {
HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
template.header(HttpHeaders.AUTHORIZATION, request.getHeader(HttpHeaders.AUTHORIZATION));
}
}

How can I see the json coming from the client when using Spring-MVC?

A client software is trying to access my Spring-MVC rest server, but it's getting a 400 (Bad Request) response every time. I know my server is fine (it's in use by many other clients), but I cannot debug the client application, so I cannot see what it is sending.
Is there a way for me to see what JSON I am receiving before Spring tries to convert it to an entity and fails? It's okay if I can only do this at debug time, I just need to be able to give support to this application's creators.
Just in case, here is the spring-mvc controller method:
#Named
#RequestMapping(value = "/taskmanager/task")
public class TaskManagerTaskRest {
#RequestMapping(value = "", method = RequestMethod.POST)
#ResponseBody
public void createTask(#RequestBody Task task, HttpServletRequest request,
HttpServletResponse response) throws CalabacinException {
// This code never gets executed because the Task json is invalid, but I don't know how I could see it.
...
...
}
}
Try to use Fiddler. It will help you to catch HTTP requests/responses. You will be able to see your JSON.
You can create and use a AbstractRequestLoggingFilter filter implementation and conditionally log the relevant parts of the request. You should use ContentCachingRequestWrapper to wrap the request.

Spring Data REST CORS - how to handle preflight OPTIONS request?

I'm using Spring Data REST to build a RESTful API. Until now my HTML GUI for this RESTful service was served from the same Tomcat and I had no problems wit Cross Origin requests.
Now I want to serve the static files from a different server. This means the API is on another domain/port. Browsers will send the OPTIONS request to get the Access-Control headers from the server. Unfortunately Spring Data REST does not handle those OPTIONS requests and even returns a HTTP 500.
I tried creating a custom controller that handles all OPTIONS requests
#Controller
#RequestMapping(value = "/**", method = RequestMethod.OPTIONS)
public class OptionsController {
#RequestMapping
public ResponseEntity options() {
return new ResponseEntity<Void>(HttpStatus.OK);
}
}
Which worked for OPTIONS, but then all other requests (like GET) ceased to work.
OPTIONS requests are switched on via the dispatchOptionsRequest dispatcher servlet parameter.
tl;dr: currently Spring Data REST does not answer OPTIONS requests at all.
It might be worth opening a ticket in our JIRA.
Browsers will send the OPTIONS request to get the Access-Control headers from the server.
Is that specified somewhere? If so, it would be cool if the ticket description included a link to that spec.
A few comments regarding your approach for a workaround:
#RequestMapping on the controller method overrides the method attribute and expectedly now matches all HTTP methods, which is why you see all requests intercepted. So you need to define OPTIONS as HTTP method there, too (or maybe instead of in the class mapping).
You're not returning any Allow header which is the whole purpose of OPTIONS in the first place.
I wonder if the approach in general makes sense as it'll be hard to reason about the supported HTTP methods in general.
Just set the parameter dispatchOptionsRequest to true into the dispatcher to process the Options method calls, into the implementation of the WebApplicationInitializer.
ServletRegistration.Dynamic dispatcher = container.addServlet("dispatcher", new DispatcherServlet(applicationContext));
dispatcher.setInitParameter("dispatchOptionsRequest", "true");
dispatcher.setLoadOnStartup(1);
dispatcher.addMapping("/*");

Ajaxupload and Spring MVC

If you upload a file via a normal form, then it works.
If you load the file / files with ajaxupload nothing works.
Error:
org.springframework.web.multipart.MultipartException: The current request is not a multipart request
Code:
#RequestMapping (value = "/ upload", method = RequestMethod.POST)
public void upload (# RequestParam MultipartFile file,
HttpServletRequest request, HttpServletResponse response)
Purpose - Multibooting files with ajax, can anyone have a working example for the Spring.
I have a separate servlet that receives HttpServletRequest and parses everything is fine. On the client side ajaxupload.
If you try a simple Spring MVC transfer request in this class, he refuses to work, arguing that the request is not multipart. Spring is obtained as a sawing the original request?
Please change fileupload.js , search and comment out the line which has "application/octet-stream"
and add following line :
xhr.setRequestHeader("Content-Type", "multipart/form-data");

Resources