2
votes

Environment Tomcat Server hosting webservice app. Website is built using SpringMVC and AngularJS.

Core issue: REST GET triggers OPTIONS request, which fails. The GET in question returns an application/JSON response. Initially, I got the error :

"Failed to load resource: the server responded with a status of 403 (Forbidden)

Failed to load http://myServer:8084/wsPRDE/navbar/getnavbartxt: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://myServer:8080' is therefore not allowed access. The response had HTTP status code 403."

Prior to this problem, all we did was have @CrossOrigin in our Controllers. Then httpd.conf was set to add those headers. Which it did for all the script-retrieving from the site (ironically not CORS) but not for the actual CORS responses. When I ensured Access-Control- Allow-Origin, Allow-Methods and Allow-Headers were returned in the response, by coding a CORSFilter into the webservice, the error changed to :

"OPTIONS http://myServer:8084/wsPOFA/navbar/getnavbartxt 403 (Forbidden) Failed to load http://myServer:8084/wsPOFA/navbar/getnavbartxt: Response for preflight has invalid HTTP status code 403."

Background: The error was detected in a rather random way. A process management colleague of mine had a windows 10 update. Since said update, the entire webservice fails because it can't be reached. Any request from the site itself to the webapplication (same address, different ports), fail. My superior (I'm an intern) then found that in Chrome and IE(when using Server alias, not when using the "normal" URL) have the same issue without the windows 10 update. Prior to the update (or when using IE with normal URL), the GET does not seem to trigger an OPTIONS request.

Further Attempts After this I've tried to expand the Allow-Headers to include all fields present in the GET request/response. I also compare the Origin in the OPTIONS request to a whitelist of Origins, setting the OPTIONS response Allow-Origin header value to the request Origin value if they match.

So the origins match and all the headers in the request/response are set to be allowed. I've spent 3 days so far reading up on CORS and looking for solutions on the web. Most solutions mention the CORS filter I've implemented, and then there are a ton of dirty-workarounds from clearing the header entirely, to always responding 200 OK to OPTIONS requests. But they seem very "dirty" solutions.

Can anyone point me in the right direction? Should I be looking at adding CORS-related code to my Spring Controllers? Is the delegation of constructing httpRequests to AngularJS the crux of my problems? Or am I thinking in the wrong direction entirely?

Currently, my request/response headers look like this: Headers

1
The server must respond with 200 instead of 403 for an OPTIONS request to be successful. Until that occurs, it will continue to fail. Modifying the cors headers will not help resolve this problem, it's server-side.Kevin B
Why do you consider responding to an OPTIONS request with 200 as "dirty"? That is exactly what needs to be done.georgeawg
@georgeawg Well, perhaps that's what I'm confused about. The preflight request is there to check if the server provides Access-Control-Allow-Method (in my case, GET) when the request comes from Access-Control-Allow-Origin. The reason I call it "dirty", is because it seems to me you don't want your server to just answer 200 on any options request. Only when the origins match and the method is allowed. The "dirty" method I found, is a RewriteValve for Apache, which just gives 200 to any OPTIONS request. If that's the only way, I'll have to do that. But I was hoping there was a cleaner way.TJanssen

1 Answers

0
votes

This question got downvoted, probably because I wasn't exactly clearminded by the time I decided to ask for help. In any case, I managed to resolve the problem today. By the time I posted the question, I had already made a Custom CORS filter at the application level. Changing this filter to manually set the Status Code to 200 for Options if the Allow-Origin and Allow-Methods fields were set and only applying the chain.doFilter() on non-options requests resolved the issue. I'm a bit unsure if it's an ideal/safe solution. But it works. Below you can find the full solution used.

web.xml:

<filter>
    <filter-name>SimpleCORSFilter</filter-name>
    <filter-class>com.ofis.webservice.CORS.SimpleCORSFilter</filter-class>
</filter>
<filter-mapping>
    <filter-name>SimpleCORSFilter</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>

Filter class:

package com.ofis.webservice.CORS;

import java.io.IOException;
import java.util.ArrayList;
import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class SimpleCORSFilter implements Filter {

    ArrayList<String> AllowedOrigins;
    ArrayList<String> AllowedMethods;

    @Override
    public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
    if (request.getMethod().equals("OPTIONS")) {
        String reqOrigin;
        reqOrigin = request.getHeader("Origin");

        for (String origin : AllowedOrigins) {
            if (origin.equals(reqOrigin)) {
                response.setHeader("Access-Control-Allow-Origin", reqOrigin);
                break;
            }
        }

        String reqMethod;
        reqMethod = request.getHeader("Access-Control-Request-Method");

        for (String method : AllowedMethods) {
            if (method.equals(reqMethod)) {
                response.setHeader("Access-Control-Allow-Method", reqMethod);
            }
        }

        response.setHeader("Access-Control-Max-Age", "3600");
        response.setHeader("Access-Control-Allow-Credentials", "true");
        response.setHeader("Access-Control-Allow-Headers", "cache-control,if-modified-since,pragma,Content-Type");

        //Checks if Allow-Method and Allow-Origin got set. 200 OK if both are set.
        if(!response.getHeader("Access-Control-Allow-Method").equals("") && !response.getHeader("Access-Control-Allow-Origin").equals("")){
            response.setStatus(200);
        }
    } else {
        chain.doFilter(req, res);
    }