Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I configure embedded Jetty to handle OPTIONS preflight requests?

I am working on a project which is using embedded Jetty (unfortunately I just "inherited" the server side of the project and am not very familiar with the use of Jetty and its configuration).

An odd case just popped up - I'll do my best to describe:

A web-based UI (using AngularJS, from a different domain, so CORS is used) sends a POST request to change the state of something on the server. This worked at some point in the past (it was last used probably a month or so ago).

Yesterday this stopped working. Inspecting the REST calls, I saw that an OPTIONS request is first being made. The content type of the POST is application/json, so based on what I have read, this is correct. I am not sure why it was previously not sent - it is possible that the company had its version of Chrome updated recently, and the old version did not send preflight requests, but that's just speculation. In any case, here's what I think is the relevant code in my application for configuring Jetty for CORS:

FilterHolder holder = new FilterHolder(new CrossOriginFilter());
holder.setInitParameter(CrossOriginFilter.ALLOWED_ORIGINS_PARAM,  "*");
holder.setInitParameter(CrossOriginFilter.ACCESS_CONTROL_ALLOW_CREDENTIALS_HEADER, "true");
appHandler.addFilter(holder, "/*", EnumSet.of(DispatcherType.REQUEST));

Everything works fine for POST requests. I can verify this by starting Chrome with the --disable-web-security flag. No OPTIONS request is sent, and the POST works as it should.

My thinking is that since it works for the POST, it's not an authorization or security issue - it's just that Jetty is not configured properly to handle the preflight request (it just returns 401).

I can't find much documentation for embedded Jetty, and which of the CrossOriginFilter constants to use as property keys in the calls to setInitParameter (and furthermore, since the 2nd argument to that method call is a String, I really have no idea how to format the values).

What parameters should I be setting on CrossOriginFilter to property handle OPTIONS requests? And if I have said anything erroneous above or made any false assumptions, please correct me! I've got very limited experience with this.

like image 677
Jer Avatar asked Sep 24 '14 17:09

Jer


2 Answers

Documentation for CrossOriginFilter:

http://www.eclipse.org/jetty/documentation/current/cross-origin-filter.html

Javadoc for CrossOriginFilter:

http://download.eclipse.org/jetty/stable-9/apidocs/org/eclipse/jetty/servlets/CrossOriginFilter.html

Actual Source Code: (sometimes this helps people understand too):

https://github.com/eclipse/jetty.project/blob/jetty-9.2.3.v20140905/jetty-servlets/src/main/java/org/eclipse/jetty/servlets/CrossOriginFilter.java

In short, you'll likely want to add OPTIONS to the allowed methods.

(Just like the javadoc says)

FilterHolder holder = new FilterHolder(new CrossOriginFilter());
holder.setInitParameter(CrossOriginFilter.ALLOWED_METHODS_PARAM, "GET,POST,HEAD,OPTIONS");
appHandler.addFilter(holder, "/*", EnumSet.of(DispatcherType.REQUEST));

Now, to address another bug you have ...

This does nothing ...

holder.setInitParameter(CrossOriginFilter.ACCESS_CONTROL_ALLOW_CREDENTIALS_HEADER,
  "true");

That is not an init parameter key. (In fact that is a header name constant for the Access-Control-Allow-Credentials) if you want to allow credentials, then do as the javadoc says.

holder.setInitParameter(CrossOriginFilter.ALLOW_CREDENTIALS_PARAM, "true");
like image 62
Joakim Erdfelt Avatar answered Oct 22 '22 18:10

Joakim Erdfelt


I solved this issue by using the following configuration for the FilterHolder:

FilterHolder cors = new FilterHolder(CrossOriginFilter.class);
cors.setInitParameter(CrossOriginFilter.ALLOWED_ORIGINS_PARAM, "*");
cors.setInitParameter(CrossOriginFilter.ACCESS_CONTROL_ALLOW_ORIGIN_HEADER, "*");
cors.setInitParameter(CrossOriginFilter.ALLOWED_METHODS_PARAM, "OPTIONS,GET,POST,HEAD");
cors.setInitParameter(CrossOriginFilter.ALLOWED_HEADERS_PARAM, "X-Requested-With,Content-Type,Accept,Origin,Cache-Control");
cors.setInitParameter(CrossOriginFilter.CHAIN_PREFLIGHT_PARAM, "false");

Chrome sends a "Cache-Control" header, if you do not allow this header with your CORS filter, then the OPTIONS request will not be responded to with the correct headers. Most examples of the CrossOriginFilter online do not include this header.

You can optionally set CHAIN_PREFLIGHT_PARAM to false (default is true). If you set it to false, the filter will respond to the request without sending the request to the Servlet. If you would like to handle the OPTIONS request yourself, you do not need to set this param.

like image 20
modelDBA Avatar answered Oct 22 '22 18:10

modelDBA