Tuesday, March 24, 2015

Spring boot and CORS (Cross Origin Resource Sharing)

Another example of use - spring boot

Sometimes a web application needs access to local resources (filesystem, printers, devices, etc).
The access is possible through Java Applets, ActiveX, browser plugins or ... a client application exposing REST API and the web application called by the client application through the Cross Domain AJAX call. This post describes how  you can create a local microserver called by browser in (about) 5 lines of code with spring boot.

The creation of a microserver with spring boot is very easy. Simply annotate your class with @RestController and your methods with @RequestMapper.  That's it.

But when the browser calls the local mircoserver as it follows:

$.ajax({
url: 'http://localhost:8080/rest/printers',
type: 'GET',
dataType: 'json',
async: true,
cache: false,
crossDomain: true,
beforeSend: function (xhr, opts) {
var auth = make_base_auth(user, password);
console.log('auth:' + auth);
xhr.setRequestHeader('Authorization', auth);
},
success: function (resp) {
$('#result').text(resp);
},
error: function(xhr) {
alert('Error!  Status = ' + xhr.status + " Message = " + xhr.statusText);
}
});

The browser raises this exception:

XMLHttpRequest cannot load http://localhost:8080/rest/printers?_=1424781952941. No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://localhost:8081' is therefore not allowed access.

In fact,  when you call the external domain the browser checks your call and it runs the CORS protocol http://www.w3.org/wiki/CORS for more details.

how can you work it out?

You can use a cors-filter  adds this dependence and registers the filter

@Bean
public FilterRegistrationBean corsFilter() {
FilterRegistrationBean registrationBean =
new FilterRegistrationBean();
CORSFilter corsFilter = new CORSFilter();
Map<String, String> params = new HashMap<>();
params.put("cors.allowed.methods",CORSFilter.DEFAULT_ALLOWED_HTTP_METHODS + ",DELETE,PUT");
params.put("cors.allowed.headers",CORSFilter.DEFAULT_ALLOWED_HTTP_HEADERS + ",authorization");
params.put("cors.logging.enabled", "true");
registrationBean.setInitParameters(params);
registrationBean.setFilter(corsFilter);
registrationBean.setUrlPatterns(Arrays.asList("/rest/*"));
return registrationBean;
}

at this point when the browser calls the external domain, the local microserver allows this call and it works.


Consideration for IE8-9

In this type of browser CORS is supported by XDomainRequest, not by XMLHttpRequest. 
This means that you need a jquery plugin jQuery-ajaxTransport-XDomainRequest to make a Cross Domain AJAX call.

IE 8-9 has more limitations. See xdomainrequest-restrictions-limitations-and-workarounds for more details and remember you must enable cross domain in security options!

To by-pass IE8-9 limitations you can add a workaround filter to microserver

@Bean
@Order(Ordered.HIGHEST_PRECEDENCE)
public FilterRegistrationBean createIE8_9XDomainRequestFilter() {
    FilterRegistrationBean registrationBean =
            new FilterRegistrationBean();
    registrationBean.setFilter(new IE8_9XDomainRequestFilter());
    registrationBean.setUrlPatterns(Arrays.asList("/rest/*"));
    return registrationBean;
}


source code

No comments:

Post a Comment