简体   繁体   中英

How to enable CORS in Grails 3.0.1

I would like to do cross origin communication using Grails in server side. The only documentation that I found is this one

https://grails.org/plugin/cors

but this is for an old version of Grails. The other documentation that I found is for spring:

https://spring.io/guides/gs/rest-service-cors/

so I added the file SimpleCorsFilter.groovy to init/myproject/ folder, but I don't know how to wire this component into resources.groovy

So, if you got here using grails 3.2.+ you can use the default way.

Go to your application.yml and add:

grails:
    cors:
        enabled: true

It will add Access-Control-Allow-Origin '*' . If you want something different, look this page

We used a normal servlet filter with an entry in resources.groovy to solve this problem:

public class CorsFilter extends OncePerRequestFilter {

    @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);
    }
}

resources.groovy:

beans = {
    corsFilter(CorsFilter)
}

This works with CORS requests using basic authentication. I wrote the Grails 2.x plugin and this seemed easier than getting it to work with Grails 3.

To be specific, here is some code that works. Notice the interceptor name must match your controller name (here, workRequest), the domain needs to be whatever you are calling from (here, localhost:8081) and it is the before() method you want:

package rest
class WorkRequestInterceptor {
boolean before() { 
    header( "Access-Control-Allow-Origin", "http://localhost:8081" )
    header( "Access-Control-Allow-Credentials", "true" )
    header( "Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE" )
    header( "Access-Control-Max-Age", "3600" )  
    true 
}

boolean after() { true }
}

I was playing around with the emberjs framework together with a Grails 3.0 rest application when I was hit by the CORS issue. Following the steps in this article http://www.greggbolinger.com/rendering-json-in-grails-for-ember-js/ helped me get further.

The article shows how you can use the new Interceptors to create a CorsInterceptor class which sets the correct headers.

class CorsInterceptor {

  CorsInterceptor() {
    matchAll()
  }

  boolean before() {
    header( "Access-Control-Allow-Origin", "http://localhost:4200" )
    header( "Access-Control-Allow-Credentials", "true" )
    header( "Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE")
    header( "Access-Control-Max-Age", "3600" )
    true
  }

  boolean after() { true }
}

This worked as expected for GET requests, but failed for POST and PUT requests. The reason for this was that an OPTIONS preflight request was sent first to http://localhost:8080/mycontroller/1234 , which in my case caused a 404 not found to be returned.

With the help from this answer https://stackoverflow.com/a/31834551 I modified the CorsInterceptor class to this instead:

class CorsInterceptor {

   CorsInterceptor() {
    matchAll()
  }

  boolean before() {
    header( "Access-Control-Allow-Origin", "http://localhost:4200" )
    boolean options = ("OPTIONS" == request.method)
    if (options) {

        header( "Access-Control-Allow-Origin", "http://localhost:4200" )
        header( "Access-Control-Allow-Credentials", "true" )
        header( "Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE")
        header( "Access-Control-Max-Age", "3600" )

        response.status = 200
    }

    true 
  }

  boolean after() { true }

}

Now POST and PUT requests were working.

You should use grails 3's new Interceptor API. ( https://grails.github.io/grails-doc/3.0.x/guide/single.html#interceptors )

You can create an interceptor with the grails create-interceptor command. For a CORS Interceptor I would use the after() method of this Interceptor, and set it up to match all requests (or just the requests you need). You can use the response object in this method, so setting the headers should be similar to the case described in the Spring documentation.

The cleanest and simplest solution for grails 3.1.x I found is to add the Apache default CorsFilter:

import org.apache.catalina.filters.CorsFilter

// Place your Spring DSL code here
beans = {
    corsFilter(CorsFilter) //Custom CorsFilter under src/main/java/ works as well. I prefer Apache though...
}

The answer from #user215556 works as well but the default setting of the Apache Cors filter is ok as well

If you need to enable CORS for endpoints provided by the Spring Security core/rest plugins you have to add this:

Bootstrap.groovy:

SpringSecurityUtils.clientRegisterFilter("corsFilter",
    SecurityFilterPosition.SECURITY_CONTEXT_FILTER.order - 1)

resources.groovy:

beans = {
    corsFilter(CorsFilter)
}

Grails 2.4.5 CORS due to HTTP/1.1 200 OK being absent in Header during Browser Preflight Request with "OPTIONS" header @JeffScottBrown

@David Tinker ( https://github.com/davidtinker/grails-cors/issues/43 )

I just installed the Cors 1.1.0 plugin to deal with CORS issues, however I am getting a CORS issue due to the browser not receiving an "http status code of 200 Ok". I am limited in terms of spring-security-core plugin versions since I am working on a legacy Grails 2.4.5 App that requires a version of spring-security-core version that only works with that CORS 1.0.1 version available in the nexus repository.

I wrote some code in my Grails 2.4.5 controller to return the 200 status code, however I don't understand why the browser from the ReactJS Front-end doesn't get it.

I can see that 200 status code from the command line with "curl" as well as when I launch the back-end in the browser.

$ curl -i -v -H "Authorization: Bearer token" http://localhost:5000/server-app/api/v1/user/xxx
*   Trying 127.0.0.1:5000...
* Connected to localhost (127.0.0.1) port 5000 (#0)
> GET /server-app/api/v1/user/qatest1 HTTP/1.1
> Host: localhost:5000
> User-Agent: curl/7.79.1
> Accept: */*
> Authorization: Bearer token
>
* Mark bundle as not supporting multiuse
< HTTP/1.1 200 OK
HTTP/1.1 200 OK
< Server: Apache-Coyote/1.1
Server: Apache-Coyote/1.1
< Set-Cookie: JSESSIONID=xxx; Path=/server-web; HttpOnly
Set-Cookie: JSESSIONID=xxx; Path=/server-app; HttpOnly
< Content-Type: application/json;charset=UTF-8
Content-Type: application/json;charset=UTF-8
< Transfer-Encoding: chunked
Transfer-Encoding: chunked
< Date: Wed, 12 Jan 2022 22:17:47 GMT
Date: Wed, 12 Jan 2022 22:17:47 GMT
<
{
  "id": xxx,
  "username": "xxx",
  "firstName": "xxx",
  "lastName": null,
  "department":
  [
    "{id:xx name:xxx, description:xxx}"
  ]
}* Connection #0 to host localhost left intact
cors.enable.logging = true // enable logging of failed requests
cors.url.pattern = '/api/v1/*'
cors.headers = ['Access-Control-Allow-Origin': 'http://localhost:3000', // Only allow access to that origin
                'Access-Control-Allow-Headers': '*',
                'Access-Control-Allow-Methods': 'GET, HEAD, TRACE, OPTIONS',
               ]
def index(){
        def users = User?.list(max:5, sort: "username", order:"asc")
        response.status = 200
        render(users as JSON)
    }
static mappings = {
        '/api/v1/users'(resources:'user')
        "/api/v1/user/$username"(controller:'user', action:"findByUsername", method: 'GET')
        ...
        }

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM