Spring Restful: Handling OPTIONS request

We are using Spring Restful, oAuth, MVC server side, Its working fine if Server and Web app running on same server.

We are running Server and Web app in different domains, we are facing invalid session error.

When we make post call browser making OPTIONS request and immediate post call is failing.

We have enable CORS Filter so that OPTIONS call is returning 200, but it is invalidating current session, so the next post call is failing.

We didn’t dispatched OPTIONs request, so its not reaching till interceptor still its invalidating session.

We have tried following solutions:

  • Dispatching OPTIONS request and handling it in interceptor. Its creating new session but not able to store it persistence. So still it is failing
  • Sending custom access token header and Session Cookie in query parameters, but we cant access session cookie in JavaScript.

I am trying to avoid creating new session on OPTIONS request, Please let us how to avoid either in Spring or in Tomcat.

We need to support this solution for hybrid mobile apps also (as it wont support cookies).

Enabling CORS:

In web.xml:

<filter>
    <filter-name>CORSFilter</filter-name>
    <filter-class>com.tip.uiux.service.framework.CORSFilter</filter-class>
</filter>
<filter-mapping>
    <filter-name>CORSFilter</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>

In CORSFilter.java

@Override
public void doFilter(ServletRequest req, ServletResponse res,
          FilterChain chain) throws IOException, ServletException {
         HttpServletResponse response = (HttpServletResponse) res;
         response.setHeader("Access-Control-Allow-Origin", "*");
         response.setHeader("Access-Control-Allow-Credentials", "true");
         response.setHeader("X-Frame-Options", "DENY");
         response.setHeader("Access-Control-Allow-Methods","POST, GET, HEAD, OPTIONS");
         response.setHeader("Access-Control-Allow-Headers",
           "Custom-Access-Token, Origin, Accept, Session-Alias, X-Requested-With, Content-Type, Access-Control-Request-Method, Access-Control-Request-Headers");
         //LOGGER.info("Request Filtered for Cross Origin Resource Sharing using CORSFilter with custom headers");
         chain.doFilter(req, res); 
}

You should also add relevant headers to the response. Please use the below CORS filter in your application.

@Component
@Order(Ordered.HIGHEST_PRECEDENCE)
public class CorsFilter implements Filter {
    @Autowired
    Environment environment;

    public CorsFilter() {
    }

    @Override
    public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
        HttpServletResponse response = (HttpServletResponse) res;

        HttpServletRequest request = (HttpServletRequest) req;

        if (Arrays.stream(this.environment.getActiveProfiles()).anyMatch(env -> (env.equalsIgnoreCase("development")))) {
            response.setHeader("Access-Control-Allow-Origin", "http://dev.yourdomain.com");
        } else if (Arrays.stream(this.environment.getActiveProfiles()).anyMatch(env -> (env.equalsIgnoreCase("production")))) {
            response.setHeader("Access-Control-Allow-Origin", "https://www.yourdomain.com");
        } else {
            response.setHeader("Access-Control-Allow-Origin", "http://localhost:8080");
        }


        response.setHeader("Access-Control-Allow-Methods", "POST, GET, PATCH, OPTIONS, DELETE");
        response.setHeader("Access-Control-Allow-Credentials", "true");
        response.setHeader("Access-Control-Max-Age", "3600");
        response.setHeader("Access-Control-Allow-Headers", "Origin, Authorization, X-Requested-With, Content-Type, Accept, Key");

        if ("OPTIONS".equalsIgnoreCase(request.getMethod())) {
            response.setStatus(HttpServletResponse.SC_OK);
        } else {
            chain.doFilter(req, res);
        }
    }

    @Override
    public void init(FilterConfig filterConfig) {
    }

    @Override
    public void destroy() {
    }
}

Please notice that filter adds some domain information to the header also (for development and production environments, default is localhost). You can provide your own domain for these section if you want to use them.

hope this helps