CORS Filter
Tips and Tricks
How to detect CORS support in a browser
The following JavaScript snipped can be used to detect CORS support in the browser:
function browserSupportsCors() { if ("withCredentials" in new XMLHttpRequest()) return true; else if (typeof XDomainRequest == "object") return true; else return false; }
The XDomainRequest object is specific to IE 8+, the other browsers extend the existing XMLHttpRequest.
For IE 8, 9: Keep all your responses as text/plain
If you want your cross-domain web service to support as many browsers as possible set the content-type of all your responses to text/plain.
Content-Type: text/plain
Internet Explorer's CORS implementation, via the XDomainRequest
object,
imposes a number of limitations on cross-domain calls, including a rule that
only "text/plain" responses are accepted. If your service returns any other
content type, e.g. "application/json", the response will be silently discarded.
Check out the following post for more information:
XDomainRequest - Restrictions, Limitations and Workarounds
Update February 2012: Good news for developers! Internet
Explorer 10 is expected to bring full CORS support through the
XMLHttpRequest
object as other browsers do. Read the
MSDN article.
CORS and browser cookies
Users occasionally report that browser cookies don't work as expected in a cross-domain setting.
Does your filter take care of sessions? For each CORS request I get a different JSESSIONID.
In order for a Java web application or service to get at a cookie, both the CORS Filter in front of it as well as the requesting JavaScript must explicitly allow this form of credential:
First, the JavaScript that initiates the XHR call must set its "withCredentials" flag to true. If not the browser will prevent the passing of cookies during the cross-domain call.
var xhr = new XMLHttpRequest(); var url = 'http://bar.other/resources/credentialed-content/'; xhr.open('GET', url, true); xhr.withCredentials = true; xhr.onreadystatechange = handler; xhr.send();
Second, the CORS Filter must also expressly state to
the browser that cookies are permitted. This is advertised
during the so called "pre-flight" request that the browser
makes before an actual request that may involve credentials,
such as cookies. The CORS Filter ships with a default
configuration where credentials, such as cookies, are marked
as allowed when responding to preflight requests. To
restrict this edit the cors.supportsCredentials
configuration parameter.
The Java CORS filter itself doesn't access the cookie headers in any way, nor does it interface to the JSESSIONID.
So, to sum up, both the calling script on the browser side as well as the CORS Filter on the server side must expressly have the credentials flag enabled for cookies to pass through.
There is a snag however and it has to do with Internet Explorer. Its XDomainRequest implementation of CORS doesn't allow cookies to be passed at all, for security reasons says Microsoft. Which is probably a good thing. So if you wish to achieve wider browser support for your cross-domain application or service you will have to use an alternative mean to cookies for storing session IDs, such as passing the token as an URL parameter.
Allowing HTTP basic authentication
If your web server requires
HTTP
basic authentication to process requests you need to tell CORS aware
browsers to let Authorization
headers through, else the
browser will not send them and authentication will fail.
To do that add the Authorization
header name to the list of
supported
headers (cors.supportedHeaders):
Authorization
Allowing subdomains
If you wish to allow CORS requests to multiple subdomains in your
domain, but for various reasons cannot list them in the filter
configuration, set the cors.allowSubdomains
parameter to true
.
For example, if the explicitly allowed origin is http://example.com
and the subdomains setting is on, then all of the following subdomain
origins will be automatically allowed too:
http://www.example.com http://webmail.example.com http://sso.example.com http://blog.example.com
Note that for a subdomain to be allowed, both its scheme (http or https) and its suffix (top domain host name + optional IP address) must match.
This feature was contributed in version 1.4.
HTTP caching / CDN support
CORS Filter 1.9.2 added support for the Vary
header to
prevent erroneous caching of CORS response headers.
Read the full discussion is at https://bitbucket.org/thetransactioncompany/cors-filter/issue/16.
Asynchronous servlet processing
The Servlet 3.0 specification added support for asynchronous processing of requests which can greatly increase the throughput of Java web applications.
Asynchronuous processing requires the following line to the added to the
CORS Filter declaration in the web.xml descriptor
:
<filter> <filter-name>CORS</filter-name> <filter-class>com.thetransactioncompany.cors.CORSFilter</filter-class> <async-supported>true</async-supported> </filter>
Automatic reconfiguration
A special variant of the CORS Filter is available which can automatically detect changes to the configuration file and reconfigure itself.
Stick the following declaration into your web.xml file to use CORS Filter variant:
<filter> <filter-name>CORS</filter-name> <filter-class>com.thetransactioncompany.cors.autoreconf.AutoReconfigurableCORSFilter</filter-class> </filter>
This filter variant must be configured
with an external Java properties file. The filter init-param
style configuration will not work here as the web.xml
file
may not be modified at runtime.
The configuration file will be polled for changes every 20 seconds. If a change is detected the filter will automatically reload itself with the new configuration. If the new configuration is invalid an error message will be printed to the server log and the filter will continue operating with its previous intact settings.
To override this interval set the cors.configFilePollInterval
system property.
For example, to set the poll interval to 5 seconds:
-Dcors.configFilePollInterval=5
Spring Bean configuration
Somewhere in your Spring configuration files, you need to initialise the bean similar to this:
<bean id="corsFilter" class="com.myproject.filters.CorsSpringFilter" > <constructor-arg > <bean class="com.thetransactioncompany.cors.CORSConfiguration" > <constructor-arg> <!-- Init the CORS filter via Spring so that properties can be extracted from anywhere--> <props> <prop key="cors.allowOrigin">${cors.allowOrigin}</prop> <prop key="cors.allowSubdomains">${cors.allowSubdomains}</prop> <prop key="cors.supportedHeaders">${cors.supportedHeaders}</prop> <prop key="cors.exposedHeaders">${cors.exposedHeaders}</prop> <prop key="cors.supportedMethods">${cors.supportedMethods}</prop> <prop key="cors.supportsCredentials">${cors.supportsCredentials}</prop> <prop key="cors.maxAge">${cors.maxAge}</prop> <prop key="cors.tagRequests">${cors.tagRequests}</prop> </props> </constructor-arg> </bean> </constructor-arg> </bean>
A Spring user still needs to provide a wrapping class along the lines of
@Component public class CorsSpringFilter implements javax.servlet.Filter { static CORSFilter corsFilter; public CorsSpringFilter(CORSConfiguration corsConfiguration) throws ServletException { corsFilter = new CORSFilter(corsConfiguration); } public CorsSpringFilter() { super(); } @Override public void init(FilterConfig filterConfig) throws ServletException { } @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { corsFilter.doFilter(request, response, chain); } @Override public void destroy() { corsFilter.destroy(); } }
Which can be configured for deployment in web.xml
thus:
<filter> <filter-name>corsFilter</filter-name> <filter-class>CorsSpringFilter</filter-class> </filter>
FireBug is your friend
The versatile FireBug add-on is an excellent mean for debugging cross-domain calls. Yes, that's another reason to get FireFox :-)
Firebug has an excellent network activity monitor which can be used to reveal the CORS-specific conversation between browser and server during a cross-domain XHR. This is great for understanding how the protocol works, particularly the behaviour of simple/actual vs. pre-flight CORS requests.
To observe a CORS request in action you need a relatively recent version of Firebug (e.g. 1.6). Click on the "Net" tab and then on the "XHR" tab, then launch your cross-domain request.
How to load the Firebug Network Monitor
For a simple/actual CORS request Firebug will then display the following headers:
Debugging a simple/actual CORS request
Note the Origin header which the browser inserted to identify the domain from which the HTML page originated. The server checks the Origin value and responds accordingly, by either allowing or denying the request.
Debugging a simple CORS response
If the server allowed the origin it replies with an Access-Control-Allow-Origin header that contains a verbatim copy of the Origin header value or * (meaning any origin). The server may also include optional CORS response headers, such as Access-Control-Allow-Credentials (to tell the browser that it accepts cookies) or Access-Control-Expose-Headers (to tell the browser which custom headers are safe to expose to the requesting script).
CORS also has the so-called preflight request which browsers can send using HTTP OPTIONS prior to the actual CORS request to query the CORS settings of a particular HTTP server, such as the supported HTTP methods, custom headers, etc.
How to test CORS requests with a single web server
How to simulate a cross-domain request if you have just a single HTTP server to play with: Create a host alias of your web server and reload the requesting page using the alias URL.