2

The backend is developed by Spring MVC and hosted in tomcat.

When I use postman dev tool in Chrome to test the endpoint (Restful), everything is working fine. The "Access-Control-Allow-Origin", "*" is successfully added in the server. like below screenshot enter image description here

But when I wrote the Ajax to make the same request. Because browser has the Origin Policy (postman has no this policy), it will first send an HTTP OPTIONS request header to the server to determine whether the actual request is safe to send. like below screenshot. enter image description here

The response header is quite different to the one I got from postman, and there is no "Access-Control-Allow-Origin", "*" in the header. I think the server just can't accept the options request.

I have tried to add the filter in tomcat web.xml by looking this link: http://enable-cors.org/server_tomcat.html

It's Only working perfectly by using dev tool like curl and postman which has no Origin policy. But it's not working on the browser by Ajax.

Please see the below filter class

public class SimpleCORSFilter implements Filter {
    public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
        HttpServletResponse response = (HttpServletResponse) res;
System.out.println("test123");
        response.setHeader("Access-Control-Allow-Origin", "*");
        response.setHeader("Access-Control-Allow-Credentials", "true");
        response.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE");
        response.setHeader("Access-Control-Max-Age", "3600");
        response.setHeader("Access-Control-Allow-Headers", "Origin, Accept, X-Requested-With, Content-Type, Access-Control-Request-Method, Access-Control-Request-Headers");
        chain.doFilter(req, res);
    }
    public void init(FilterConfig filterConfig) {}
    public void destroy() {}
}

I use the System.out.println("test123"); in the filter, when using postman or curl, it printed the 'test123' in the console, but didn't print when using browser, so I guess the browser send the OPTIONS request, and the filter didn't intercept it, or other things intercept the request from browser first?

Can anyone help me please? Thanks.

Xiang
  • 143
  • 1
  • 4
  • 10

2 Answers2

4

If you are using tomcat you can have tomcat's implementaion in your web.xml:

<filter>
  <filter-name>CorsFilter</filter-name>
  <filter-class>org.apache.catalina.filters.CorsFilter</filter-class>
</filter>
<filter-mapping>
  <filter-name>CorsFilter</filter-name>
  <url-pattern>/*</url-pattern>
</filter-mapping>

Or In my application I did a filter(updated) like below:

public class CorsFilter implements Filter {


    @Override
    public void init(FilterConfig filterConfig) throws ServletException {

    }

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        HttpServletResponse response = (HttpServletResponse) servletResponse;
        response.setHeader("Access-Control-Allow-Origin", "*");
        response.setHeader("Access-Control-Allow-Credentials", "true");
        response.setHeader("Access-Control-Allow-Methods", "POST, GET, HEAD, OPTIONS");
        response.setHeader("Access-Control-Allow-Headers", "Origin, Accept, X-Requested-With, Content-Type, Access-Control-Request-Method, Access-Control-Request-Headers");
      if ("OPTIONS".equalsIgnoreCase((HttpServletRequest) servletRequest.getMethod())) {
        response.setStatus(HttpServletResponse.SC_OK);
      } else {
         filterChain.doFilter(servletRequest, response);
      }

    }

    @Override
    public void destroy() {

    }
}

Also please note that you should not annotate filter class with Spring's annotations like @Component

Or better you can go with Spring's CORS support

Tom Sebastian
  • 3,373
  • 5
  • 29
  • 54
  • Hi Tom, I changed the filter like you said. It shows the correct header in postman, but still not working in browser. The response of the OPTIONS request has no correct header. screenshot: http://prntscr.com/9hmpzr The response header in postman is: http://prntscr.com/9hmr5x which is working in postman – Xiang Dec 23 '15 at 13:16
  • @Xiang To confirm it's browser origin policy, if you are using Chrome can you add any CORS enabling extension and try AJAX.It is not a solution just to confirm the findings or there is any problem with the ajax request – Tom Sebastian Dec 23 '15 at 17:08
  • @Xiang here is a similar issue. possibly it may help http://stackoverflow.com/questions/30632200/standalone-spring-oauth2-jwt-authorization-server-cors/30638914#30638914. Same updated in the answer – Tom Sebastian Dec 23 '15 at 17:25
  • Did anyone find a solution to this issue? I am facing the same problem. – tarekahf Nov 12 '18 at 16:15
  • Yes, finally, it worked at my end only by adding this header ``response.setHeader("Access-Control-Allow-Origin", "*")`` to the servlet. The issue is that when I was testing the servlet from Javascript/browser, I was passing extra unnecessary header ``Authorization`` code. When I removed this part, it worked right away. The trick is to use simple/minimum code in Javascript to test the servlet. I used ``var request = new XMLHttpRequest();`` with ``get`` and simple URL, and it worked like a charm. – tarekahf Nov 12 '18 at 17:22
3

Using an response.setHeader("Access-Control-Allow-Origin", "*"); Will not work. You will need to put the explicit requesting URL. Take the origin from the request to the servlet and dynamically replace on each request.

Here's a code snippet that I use to solve this problem:

    String clientOrigin = request.getHeader("origin");
    response.setHeader("Access-Control-Allow-Origin", clientOrigin);
    response.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS");
    response.setHeader("Access-Control-Max-Age", "3600");
    response.setHeader("Access-Control-Allow-Credentials", "true");
    response.setHeader("Access-Control-Allow-Headers", "x-requested-with,Access-Control-Allow-Origin,CopyStreamException,Access-Control-Allow-Methods,Access-Control-Max-Age");

EDIT 1: I've looked at my code again, and I saw that I also added filters to my web.xml as mentioned by @Tom-Sebastian

Here what I have in the web.xml:

<filter>
    <filter-name>CORS</filter-name>
    <filter-class>com.thetransactioncompany.cors.CORSFilter</filter-class>
</filter>
<filter-mapping>
    <filter-name>CORS</filter-name>
    <servlet-name>myServlet</servlet-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>

Hope it will help,
Liron

Liron
  • 515
  • 4
  • 20