Grails plugin to add Cross-Origin Resource Sharing (CORS) headers for Grails applications. These headers make it possible for Javascript code served from a different host to easily make calls to your application.
It is not easy to do this in a Grails application due to the following bug: http://jira.grails.org/browse/GRAILS-5531
This plugin does not work with Grails 3. To handle CORS in a Grails 3 project you can just create a servlet filter
somewhere under src/main/java
:
@Priority(Integer.MIN_VALUE)
public class CorsFilter extends OncePerRequestFilter {
public CorsFilter() { }
@Override
protected void doFilterInternal(HttpServletRequest req, HttpServletResponse resp, FilterChain chain)
throws ServletException, IOException {
String origin = req.getHeader("Origin");
boolean options = "OPTIONS".equals(req.getMethod());
if (options) {
if (origin == null) return;
resp.addHeader("Access-Control-Allow-Headers", "origin, authorization, accept, content-type, x-requested-with");
resp.addHeader("Access-Control-Allow-Methods", "GET, HEAD, POST, PUT, DELETE, TRACE, OPTIONS");
resp.addHeader("Access-Control-Max-Age", "3600");
}
resp.addHeader("Access-Control-Allow-Origin", origin == null ? "*" : origin);
resp.addHeader("Access-Control-Allow-Credentials", "true");
if (!options) chain.doFilter(req, resp);
}
}
Reference the filter in grails-app/conf/spring/resources.groovy
:
beans = {
corsFilter(CorsFilter)
}
Add a dependency to BuildConfig.groovy:
plugins {
runtime ":cors:1.3.0"
...
}
The default configuration installs a servlet filter that adds the following headers to all OPTIONS requests:
Access-Control-Allow-Origin: <value of Origin header>
Access-Control-Allow-Credentials: true
Access-Control-Allow-Headers: origin, authorization, accept, content-type, x-requested-with
Access-Control-Allow-Methods: GET, HEAD, POST, PUT, DELETE, TRACE, OPTIONS
Access-Control-Max-Age: 3600
The 'Access-Control-Allow-Origin' and 'Access-Control-Allow-Credentials' headers are also added to non-OPTIONS requests (GET, POST et al.). If the plugin is configured to produce an 'Access-Control-Expose-Headers' header, it will be added to non-OPTIONS requests as well.
These headers permit Javascript code loaded from anywhere to make AJAX calls to your application including specifying an 'Authorization' header (e.g. for Basic authentication). The browser will cache the results of the OPTIONS request for 1 hour (3600 seconds).
The CORS plugin is configured through Config.groovy.
You can limit the URL patterns the filter is applied to:
cors.url.pattern = '/rest/*'
This parameter also accepts a list of patterns to match:
cors.url.pattern = ['/service1/*', '/service2/*']
Due to the 'Stringy' nature of external properties files, url patterns can be configured using a comma seperated string such as:
cors.url.pattern = /api/*, /api-docs/*
You can override the default values used for the headers by supplying a headers map:
cors.headers = [
'Access-Control-Allow-Origin': 'http://app.example.com',
'My-Custom-Header': 'some value']
Due to the 'Stringy' nature of external properties files, headers can be configured using a single line 'string' map:
cors.headers = ['Access-Control-Allow-Origin': 'http://app.example.com','My-Custom-Header': 'some value']
Note that if you want to specify more than one host for 'Access-Control-Allow-Origin' there are issues with browser support. The recommended approach is to check the 'Origin' header of the request and echo it back if you are happy with it. The CORS plugin implements this using a regex to match allowed origins:
cors.allow.origin.regex = '.*\\.example\\.com'
If 'Origin' header matches the regex then it is echoed back as 'Access-Control-Allow-Origin' otherwise no CORS headers are sent back to the client and the browser will deny the request.
Note that you can always send back '*' instead of echoing the 'Origin' header by including:
cors.headers = ['Access-Control-Allow-Origin': '*']
This can be combined with cors.allow.origin.regex to limit allowed domains.
You can specify a comma-delimited list of response headers that should be exposed to the client:
cors.expose.headers = 'X-app-header1,X-app-header2'
You can accept any Access-Control-Request-Headers as follows:
cors.headers = ['Access-Control-Allow-Headers': '*']
You can enable logging of failed requests:
cors.enable.logging = true
You can disable the filter if needed. Note that the filter defaults to enabled.
cors.enabled = false
The browser only has to make an extra OPTIONS request for a cross-site AJAX GET if additional headers are specified. So this cross site jQuery AJAX call does not result in an OPTIONS request:
$.ajax("https://api.notmyserver.com/rest/stuff/123", {
data: data,
dataType: 'json',
type: 'get',
success: callback,
});
Whereas this one does (at least the first time):
$.ajax("https://api.notmyserver.com/rest/stuff/123", {
data: data,
dataType: 'json',
type: 'get',
success: callback,
headers: {Authorization: "Basic ..."}
});
So if you can authenticate or whatever using query parameters instead of headers it can reduce latency.
Copyright 2013-2022 DataEQ (http://www.dataeq.com/)
Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.
1.3.0:
1.2.0:
1.1.9:
1.1.8:
1.1.7:
1.1.6:
1.1.5:
1.1.4:
1.1.3:
1.1.2:
1.1.1: Now works with Spring Security basic authentication (thanks James Hardwick)
1.1.0:
1.0.4: Added Access-Control-Allow-Credentials: true
1.0.3: Bumped version no. to workaround plugin publishing issue
1.0.2: Added Access-Control-Allow-Methods header to OPTIONS request
1.0.1: Added Content-Type to default Access-Control-Allow-Headers