Skip to content

Enable/Disable Spring CORS option [SPR-13691] #18266

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
spring-projects-issues opened this issue Nov 16, 2015 · 11 comments
Closed

Enable/Disable Spring CORS option [SPR-13691] #18266

spring-projects-issues opened this issue Nov 16, 2015 · 11 comments
Assignees
Labels
in: web Issues in web modules (web, webmvc, webflux, websocket) status: declined A suggestion or change that we don't feel we should currently apply type: enhancement A general enhancement

Comments

@spring-projects-issues
Copy link
Collaborator

spring-projects-issues commented Nov 16, 2015

SPT Developer opened SPR-13691 and commented

I am using my original CORS interceptor and dispatching OPTIONS request for pre-flight.

servlet.setDispatchOptionsRequest(true);

After upgraded Spring Boot to 1.3.0 RC1 with Spring 4.2.2, Spring CORS intercepted my original. In order to disable Spring CORS, I added CorsUtils in my classpath temporary.

package org.springframework.web.cors;

import javax.servlet.http.HttpServletRequest;

/**
 * CORS utility (Overriding)
 */
public class CorsUtils {
	
	/**
	 * Returns {@code true} if the request is a valid CORS one.
	 */
	public static boolean isCorsRequest(HttpServletRequest request) {
		// Disable Spring CORS by force.
		return false;
	}
	
	/**
	 * Returns {@code true} if the request is a valid CORS pre-flight one.
	 */
	public static boolean isPreFlightRequest(HttpServletRequest request) {
		// Disable Spring CORS by force.
		return false;
	}
}

To fix it permanently, please add option to swith on/off Spring CORS.

if (!enableSpringCors || !CorsUtils.isCorsRequest(request)) {
	......
if (enableSpringCors && CorsUtils.isCorsRequest(request)) {
	......
if (this.dispatchOptionsRequest || (enableSpringCors && CorsUtils.isPreFlightRequest(request))) {
	......
if (enableSpringCors && CorsUtils.isPreFlightRequest(request)) {
	......

Affects: 4.2.2

Issue Links:

0 votes, 5 watchers

@spring-projects-issues
Copy link
Collaborator Author

Sébastien Deleuze commented

Without overriding CorsUtils, could you detail where Spring CORS default processing (with no CORS configuration done at Spring level) prevents yours to work since the CORS related check in FrameworkServlet is suitable for your use case too (it avoids you to have to enable explicitly dispatchOptionsRequest) and since there is a check in DefaultCorsProcessor that prevents Spring CORS processing to be applied if there is already CORS response headers set by, for example, your own CORS interceptor.

I would like to see if there is a way to make your CORS interceptor works without such option, by ensuring it is applied before Spring CorsInterceptor and PreFlightHandler.

@spring-projects-issues
Copy link
Collaborator Author

Rossen Stoyanchev commented

Indeed the CORS support was designed to have no effect if an existing Filter or Interceptor-based solution is in place. So perhaps there is a way. It would also be good to hear if there is anything that might be missing in our present CORS support.

@spring-projects-issues
Copy link
Collaborator Author

SPT Developer commented

Thank you for replying.

I created sample project and tested in detail. Then, my original code caused this problem and Spring CORS did not prevent mine exactly.

@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
	
	// Check handler, and return quickly if handler is not 'HandlerMethod'.
	if (!HandlerMethod.class.isInstance(handler)) {
		return true;
	}
	
	RequestMapping mapping = ((HandlerMethod) handler).getMethod().getAnnotation(RequestMapping.class);
	......

Sorry for mistaken description before!

But I found Spring CORS rewrite handler. I hope 'HandlerMethod' come to my interceptor when request is pre-flight.

// Chain contains 'HandlerMethod'
HandlerExecutionChain executionChain = getHandlerExecutionChain(handler, request);
if (CorsUtils.isCorsRequest(request)) {
	CorsConfiguration globalConfig = this.corsConfigSource.getCorsConfiguration(request);
	CorsConfiguration handlerConfig = getCorsConfiguration(handler, request);
	CorsConfiguration config = (globalConfig != null ? globalConfig.combine(handlerConfig) : handlerConfig);
	executionChain = getCorsHandlerExecutionChain(request, executionChain, config);
}
// Chain contains 'PreFlightHandler'
return executionChain;
if (CorsUtils.isPreFlightRequest(request)) {
	HandlerInterceptor[] interceptors = chain.getInterceptors();
	// Handler in chain was rewrited to 'PreFlightHandler'
	chain = new HandlerExecutionChain(new PreFlightHandler(config), interceptors);
}

@spring-projects-issues
Copy link
Collaborator Author

Sébastien Deleuze commented

For CORS pre-flight requests, the handler is changed to PreFlightHandler``, but your CORS interceptor should still be invoked before the handler since Spring reuse the interceptors of the original HandlerExecutionChain`. I am not sure what prevents it, could you detail what happen?

@spring-projects-issues
Copy link
Collaborator Author

SPT Developer commented

If Spring CORS to be force-disabled (or use Spring 4.1.X), my CORS interceptor receive "handler" as HandlerMethod, and create Access-Control-* header. My interceptor use HandlerMethod for reading RequestMapping annotation and generating Access-Control-Allow-Methods header.

[Response]
HTTP/1.1 200 OK
Access-Control-Allow-Credentials: true
Access-Control-Allow-Headers: *
Access-Control-Allow-Methods: POST,OPTIONS
Access-Control-Allow-Origin: (Origin)
Access-Control-Max-Age: 3600
Content-Length: (Bytes)
Date: (Date)
Server: Jetty

If Spring CORS to be enabled as default (use Spring 4.2.X), my CORS interceptor receive not HandlerMethod but PreFlightHandler, and cannot create Access-Control-* header because PreFlightHandler does not contain RequestMapping annotation.

[Response]
HTTP/1.1 200 OK
Content-Length: (Bytes)
Date: (Date)
Server: Jetty

In conclusion, Spring CORS prevent my CORS interceptor to create Access-Control-* header. (Does it make sense?)

@spring-projects-issues
Copy link
Collaborator Author

Sébastien Deleuze commented

In Spring Framework 4.2+, our strategy is to support custom CORS handling by disabling our builtin CORS support if we detect some CORS response headers set by custom filters or interceptors, rather than providing an option to disable CORS totally.

In your case, that does not works because your CORS support is pretty advanced and uses, like Spring builtin CORS support, @RequestMapping annotations to generate Access-Control-Allow-Methods.

From my POV, this is a use case we don't support. With Spring Framework 4.2+, if you need CORS support based on @RequestMapping annotations, I would advise you to use Spring builtin support that provides that functionality out of the box.

If your custom CORS support provides some functionalities that are missing in Spring Framework CORS support, feel free to create an issue for that.

We make our best to ensure backward-compatibility, but for this kind of advanced use cases that we now implement natively, I think it is better to rely on our native support rather than adding an additional option not needed in most use cases. I hope that makes sense for you.

@spring-projects-issues
Copy link
Collaborator Author

SPT Developer commented

Thank you for detailed explanation.

I will try to replace original implementation to Spring builtin.

@spring-projects-issues
Copy link
Collaborator Author

Kristoffer Peterhänsel commented

So. Small problem with not being able to disable the CORS processing.

We are running a number of Spring Boot-based micro services with one running as an API gateway in front of it.

As a spearhead for starting to move to Spring Boot 1.3 we implemented a new service with that. And since it implements some webhooks for a service it integrates with, we implemented a Zuul route from the API Gateway service to the new service.

Now the problem is that we has to manually handle CORS in the API Gateway since it is Spring Boot 1.2. And we have to use Spring Cloud Netflix 1.0 for the same reason. So we add the headers and we have no way to configure Zuul to strip out headers (upstream or downsteam) since that came in Spring Cloud Netflix 1.1. And we have no way to stop our new service from sending CORS headers. That leaves us with incorrect double CORS headers.

Now obviously we would like to upgrade our API gateway as well. But doing so is not trivial and not really in the scope of the task being worked on.

I am not really sure why CORS handling should be a forced feature anyway. Sure it is great to have it baked into the framework. It's something we all need when we deal with browsers. But for fully internal services it makes very little sense. And as demonstrated, can make a fairly simple problem very hard and time-consuming to fix

@spring-projects-issues
Copy link
Collaborator Author

Rossen Stoyanchev commented

The feature isn't meant to be forced based on strong beliefs or anything like that. For sure we didn't set out to do that and don't see it that way. It's just that proper integration into the HandlerMapping hierarchy also means it's practically difficult to co-exist with other custom solutions built at the same level.

There is actually a way to suppress CORS by making Spring believe there is a more global CORS solution in place already (e.g. a CORS filter from Tomcat, Jetty, etc). For your new Spring Boot 1.3 service, register a Filter that wraps the response and returns a value for the "Access-Control-Allow-Origin" header, and the CORS support in Spring will not be applied.

It's not as good as an on-off config option but seems fitting for a transitional situation like this.

@spring-projects-issues
Copy link
Collaborator Author

Sébastien Deleuze commented

Kristoffer Peterhänsel You have also already a way to totally disable CORS processing by providing a no-op implementation of CorsProcessor with RequestMappingHandlerMapping#setCorsProcessor(). With Spring Boot, I think the best way to do that is to register a BeanPostProcessor bean with a if (bean instanceof RequestMappingHandlerMapping) test that will set the custom CORS processor.

@spring-projects-issues
Copy link
Collaborator Author

Kristoffer Peterhänsel commented

Yeah okay. That works. A somewhat complicated workaround. But it works.

So thanks for the suggestion Sébastien

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
in: web Issues in web modules (web, webmvc, webflux, websocket) status: declined A suggestion or change that we don't feel we should currently apply type: enhancement A general enhancement
Projects
None yet
Development

No branches or pull requests

2 participants