Let’s say that you have created a REST API in your Java EE WEB Application and you want to use it in HTML pages through AngularJS, jQuery or even plain JS. If you were newb (like I was), you would expect that this is going to work out of the box (as is) since your API has already been used in many cross platform clients without any problem.
Unfortunately though, for Murphy’s sake, this is not the case. Due to Same-origin policy http requests that are made in browsers must be done with a very specific way in order to work when the caller (web site) is on different domain than the web service (REST API).
In order to make it work, the caller must perform CORS HTTP request to the WEB service and the web service subsequently must be modified in order to support CORS requests.
When I first came across to that problem I thought that this would be a-half-an-hour-problem but it wasn’t. I had to spend a full day on this (thankfully on weekend) until the problem was completely “solved and working” on every modern browser.
For your web services, to support CORS requests you must deal with 2 problems.
1) CORS HEADERS
The first problem is about adding some additional HTTP headers (CORS) in your HTTP responses. This is relatively easy to find since googling about CORS almost everyone will tell about this. The new CORS headers will “tell” the caller if you support CORS requests and some other things like from which origin or which headers you accept as valid.
For example the header with value “Access-Control-Allow-Origin” expects the valid caller domain names. If the caller site is on domain mydomain.com, then the value of that header should be “http://mydomain.com”. Alternatively in order to indicate that callers from any domain can access your resource you may just use the value “*”.
If your application requires authentication (which was my case), the header “Access-Control-Allow-Headers” should also include the value “Authorization”.
2) Preflighted requests
The second one is about Preflighted requests. This was very tricky and hard to find since I only saw that happen on Firefox and Chrome – which in my opinion are the most important browsers out there. These two browsers first send an HTTP request by the OPTIONS method to the resource on the other domain, in order to determine whether the actual request is safe to send. Cross-site requests are preflighted like this since they may have implications to user data. In particular, a request is preflighted if:
– It uses methods other than GET, HEAD or POST. Also, if POST is used to send request data with a Content-Type other than application/x-www-form-urlencoded, multipart/form-data, or text/plain, e.g. if the POST request sends an XML payload to the server using application/xml or text/xml, then the request is preflighted.
– It sets custom headers in the request.
In order to overcome both of the problems you must change your web.xml file and also create a custom class that implements javax.servlet.Filter interface.
This is what I had to do in order to overcome Problem 1:
As you can see I created a Filter Class that accepts from web.xml one parameter which is the permitted origin and I used * as a value. The specific filter will intercept all of your application responses and add the custom CORS headers.
To overcome Problem 2 you will also need to modify your web.xml and in your security-constraint tag under web-resource-collection add all the HTTP method except OPTIONS. This will remove your current security from OPTIONS requests and actually make preflight requests feasible.
Here is a sample of what I did in our API:
I have excluded OPTIONS request from authentication filter. If you don’t do that your requests will always get a 401 error although the security on client is set correctly.