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("/*");
Related
Basic Spring Security is great as it comes with WebSecurity (preventing malformed URLs) along with setting up authentication and authorization.
As part of authenticating my web request, the digest of the request body needs to be verified against the digest value passed in the http header. Setting up the Spring Security filter, forces my auth filter to process a firewalled request. The firewalled request doesn't seem to expose the request body to the filter.
What is the correct way to set up the Spring Security filter so that I can inspect the request body?
Thanks!
In Spring Security there are many filter classes built in for to be extended and used for specific purposes. As in my experience most of (or all of them) have methods with overloads which have access to,
HttpServletRequest request, HttpServletResponse response
as method arguments, so that those can be used inside the method.
When the filter class is met with any request, these variables are then populated with related data thus the code inside the methods output as expected.
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));
}
}
I am currently working on a RESTeasy based RESTful service. I have a filter class which serves as a server request filter as well as a server response filter (i.e. it implements ContainerRequestFilter and ContainerResponseFilter interfaces).
At the beginning of the request, I use the filter to put an object into ThreadLocal. This object is used by the resources throughout the request. At the end of the request, before sending out the response, the filter removes the object from ThreadLocal.
My question is that is there a guarantee that the the request filter, the resource and the response filter will all execute in the same thread? Is there a possibility that after the request filter puts the object into ThreadLocal, a different thread will execute the request (and thus not have access to the object)?
I was sure that this was the case but then I saw this http://jersey.576304.n2.nabble.com/Does-filter-method-of-ContainerRequestFilter-run-in-resource-method-thread-td7582648.html (official Jersey forum) and now I have doubts.
javax.ws.rs.container.ContainerRequestContext.setProperty(...)
and
javax.ws.rs.container.ContainerRequestContext.getProperty(...)
are probably the right approach. The javadoc states:
In a Servlet container, the properties are synchronized with the ServletRequest and expose all the attributes available in the ServletRequest. Any modifications of the properties are also reflected in the set of properties of the associated ServletRequest.
I initially implemented a controller to handle OPTIONS requests and apply the headers required for CORS to the response. Recently I have been cleaning up code and created an interceptor for the CORS stuff. Part of that cleanup would be to remove the controller methods for the OPTIONS requests.
However, my mockmvc tests require the OPTIONS handling methods to be there, while when I actually run the app the interceptor chain handles the OPTIONS requests, even though there is no controller method for the OPTIONS request.
I would expect a test like this
MockHttpServletRequestBuilder optionsRequest = options("the-url");
this.mockMvc.perform(optionsRequest).
andExpect(status().isOk()).
andExpect(header().string("Access-Control-Allow-Origin", "*")).
andExpect(header().string("Access-Control-Allow-Methods", "OPTIONS,GET,HEAD,POST,PUT,DELETE,TRACE,CONNECT")).
andExpect(header().string("Access-Control-Allow-Headers", "content-type"));
to pass if my test setup has
StandaloneMockMvcBuilder builder = MockMvcBuilders.standaloneSetup(this.controller);
builder = builder.dispatchOptions(true);
builder = builder.addInterceptors(new CorsInterceptor());
either that or I expect I would not be seeing the cross origin requests working on the running app.
I am currently developing an OData service using Web Api 2 and EF6 with a Code First Approach. My controllers inherit from the normal ApiController Base.
I have decorated my action methods with the Queryable attribute and have also enabled Query Support in the WebApiConfig file. Through my CORS policy, I have specified the DataServiceVersion and MaxDataServiceVersion as part of my Accept and Exposed Headers.
Strangely, my odata endpoint seems to not return the DataServiceVersion as part of the response header but, if my controllers inherit from the ODataController base I am able to see it in the response.
Is there a way to enable this header while using ApiController as the base.
This header is needed as datajs requires it on the client side.
First to answer your question:
Yes, you can expose the DataServiceVersion http header yourself. It's custom code though, not a setting on an existing component.
Add a "Filter" to your global http configuration. A filter is a class derived from "System.Web.Http.Filters.ActionFilterAttribute".
for example;
internal class DataServiceVersionHeaderFilterWebAPI : System.Web.Http.Filters.ActionFilterAttribute
{
public override void OnActionExecuted(HttpActionExecutedContext actionExecutedContext)
{
actionExecutedContext.Response.Content.Headers.Add("DataServiceVersion", "3.0");
actionExecutedContext.Response.Content.Headers.Add("Access-Control-Expose-Headers", "DataServiceVersion");
}
}
Then configure this filter to be used (in application start of global.asax)
GlobalConfiguration.Configuration.Filters.Add( new DataServiceVersionHeaderFilterWebAPI() );
This will allow your cross domain OData query from a security perspective. There is however another issue with this;
OData is a specification larger than just the request URI's & HTTP headers. It also specifies how to exchange model information and the actual data exchange is a predefined object structure. Simple, but still a predefined structure.
object.d = service returned content
You will have to implement all those pieces of the specification ($filter,$metadata,$top, return formats, etc) yourself.
Some food for thought.