Skip to content
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

CORS support [SPR-9278] #13916

Closed
spring-projects-issues opened this issue Mar 28, 2012 · 23 comments
Closed

CORS support [SPR-9278] #13916

spring-projects-issues opened this issue Mar 28, 2012 · 23 comments
Assignees
Labels
has: votes-jira Issues migrated from JIRA with more than 10 votes at the time of import in: web Issues in web modules (web, webmvc, webflux, websocket) type: enhancement A general enhancement
Milestone

Comments

@spring-projects-issues
Copy link
Collaborator

spring-projects-issues commented Mar 28, 2012

Keith Donald opened SPR-9278 and commented

Cross origin resource sharing (CORS) is a relevant spec these days with the emergence of HTML5 & JS clients that consume data via REST APIs. For a given app, in many cases the host that serves the JS (e.g. foo.com) is different than the host that serves the data (e.g. api.foo.com). In this case, CORS can enable the cross-domain communication.

It would be useful if Spring MVC provided code & guidance on how to configure CORS when implementing a Java-backed REST API consumed by JS clients located in other domains. Prior work exists that would be a candidate for integration.

See the resources below:
CORS Spec: http://www.w3.org/TR/cors/
Basic example of a CorsFilter implementation: https://gist.github.com/2232095
What appears to be a full-featured CorsFilter implementation: https://bitbucket.org/jsumners/corsfilter


Affects: 3.1.1

Sub-tasks:

Issue Links:

26 votes, 34 watchers

@spring-projects-issues
Copy link
Collaborator Author

Keith Donald commented

There is a typo above. The "full featured CORS implementation" link should be: https://bitbucket.org/jsumners/corsfilter. It would help if issues and comments were editable to correct typos (it's actually quite aggrevatong they are not).

@spring-projects-issues
Copy link
Collaborator Author

Marcel Overdijk commented

Note that Jetty has a nice CrossOriginFilter as well: http://grepcode.com/file/repo1.maven.org/maven2/org.eclipse.jetty/jetty-servlets/7.1.4.v20100610/org/eclipse/jetty/servlets/CrossOriginFilter.java

But I agree with Keith that it would be very nice if Spring MVC offers something out of the box here.

@spring-projects-issues
Copy link
Collaborator Author

David Bellem commented

For anyone stumbling upon this now, I ended up using:
http://software.dzhuvinov.com/cors-filter.html

Just add a filter in the web.xml and it works quite well. Moving this into Spring MVC would have made my life easier.

@spring-projects-issues
Copy link
Collaborator Author

Russell Allen commented

I was able to add dynamic CORS pre-flight support to Spring 4 (should work with 3.1+) by overriding RequestMappingHandlerMapping's registerHandlerMethod such that it registers a dynamically created handler for the pre-flight OPTIONS request to the same url pattern on the primary request handler method. Here's the code for future reference:

public class CrossOriginRequestMappingHandlerMapping extends RequestMappingHandlerMapping {

	private final Method corsMethod;
	private final RequestMappingInfo baseCorsRequestMappingInfo;
	public static final String CORS_REQUEST_ATTRIBUTE_MARKER = "CROSS_ORIGIN_REQUEST";
	
	public CrossOriginRequestMappingHandlerMapping() throws NoSuchMethodException, SecurityException {
		this.corsMethod = CorsPreflightController.class.getDeclaredMethods()[0];
		this.baseCorsRequestMappingInfo = getMappingForMethod(corsMethod, CorsPreflightController.class);
	}
	
	@Override
	public void afterPropertiesSet() {
		super.afterPropertiesSet();
		//decorate the adapter's return value handlers with some custom code that sets the CORS response headers.
		RequestMappingHandlerAdapter adapter = getWebApplicationContext().getBean(RequestMappingHandlerAdapter.class);
		 /* RequestMappingHandlerAdapter aggressively hides the handlers in a HandlerMethodArgumentResolverComposite
		 *  which it effectively forces you to use.  Also, it will throw a NPE if you try to getReturnValueHandlers() 
		 *  before the afterPropertiesSet() is called.  So, we have to let it build its HandlerMethodArgumentResolverComposite,
		 *  then get and decorate the resolvers, then set the new list which triggers a rebuild of the 
		 *  HandlerMethodArgumentResolverComposite.  It's a bit obtuse, but it works.
		 */
		List<HandlerMethodReturnValueHandler> existingHandlers = adapter.getReturnValueHandlers();
		List<HandlerMethodReturnValueHandler> newHandlers = new ArrayList<>(existingHandlers.size());
		for (HandlerMethodReturnValueHandler existingHandler : existingHandlers) {
			newHandlers.add(new HandlerMethodReturnValueHandlerInterceptor(existingHandler) {
				@Override
				protected void preHandle(Object returnValue, MethodParameter returnType, ModelAndViewContainer mavContainer, NativeWebRequest webRequest) {
					CrossOrigin crossOrigin = (CrossOrigin) webRequest.getAttribute(CORS_REQUEST_ATTRIBUTE_MARKER, RequestAttributes.SCOPE_REQUEST);
					if (crossOrigin != null) {
						//While the handler supports CORS, we still need to see if the request origin is in the allowed set...
						String[] allowedOrigins = crossOrigin.origin();
						Arrays.sort(allowedOrigins);
						String requestedOrigin = webRequest.getHeader("Origin");
						if (Arrays.binarySearch(allowedOrigins, requestedOrigin) >= 0 || Arrays.binarySearch(allowedOrigins, "*") >= 0) {
							HttpServletResponse response = webRequest.getNativeResponse(HttpServletResponse.class);
							response.setHeader("Access-Control-Allow-Origin", webRequest.getHeader("Origin"));
							if (crossOrigin.header().length > 0) response.setHeader("Access-Control-Expose-Headers", StringUtils.arrayToDelimitedString(crossOrigin.header(), ", "));
							if (crossOrigin.allowCredentials()) response.setHeader("Access-Control-Allow-Credentials", "true");
						}
					}
				}
			});
		}
		adapter.setReturnValueHandlers(newHandlers);
	}
	
	@Override
	protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception {
		HandlerMethod handlerMethod = super.getHandlerInternal(request);
		CrossOrigin crossOrigin = handlerMethod.getMethodAnnotation( CrossOrigin.class);
		if (crossOrigin != null) {
			request.setAttribute(CORS_REQUEST_ATTRIBUTE_MARKER, crossOrigin);
		}
		return handlerMethod;
	}
	
	@Override
	protected void registerHandlerMethod(Object handler, Method method, RequestMappingInfo primaryMapping) {
		//register the primary handler as usual...
		super.registerHandlerMethod(handler, method, primaryMapping);
		//if this handler is also a CrossOrigin handler, then register a symbiotic CORS handler
		CrossOrigin crossOrigin = AnnotationUtils.findAnnotation(method, CrossOrigin.class);
		if (crossOrigin != null) {
			//create the CORS handler's RequestMappingInfo using the path pattern from the primary handler. 
			RequestMappingInfo corsMapping = baseCorsRequestMappingInfo.combine(new RequestMappingInfo(primaryMapping.getPatternsCondition(), null, null, null, null, null, null));
			//create the handler (curry some information from primary handler)...
			RequestMapping requestMapping = AnnotationUtils.findAnnotation(method, RequestMapping.class);
			CorsPreflightController corsHandler = new CorsPreflightController(
					crossOrigin.origin(), 
					requestMapping.method(), 
					primaryMapping.getHeadersCondition() == null ? 
							Collections.<NameValueExpression<String>>emptySet() 
							: primaryMapping.getHeadersCondition().getExpressions(), 
					crossOrigin.allowCredentials(), 
					crossOrigin.maxAge());
			//finally, register the CORS mirror request handler...
			super.registerHandlerMethod(corsHandler, corsMethod, corsMapping);
		}
	}
	
	
	private static class CorsPreflightController {

		private String[] allowedOrigins;
		private RequestMethod[] allowedMethods;
		private Set<NameValueExpression<String>> headerExpressions;
		private boolean allowCredentials;
		private int maxAge;
		
		private CorsPreflightController(
				String[] allowedOrigins, 
				RequestMethod[] allowedMethods, 
				Set<NameValueExpression<String>> headerExpressions, 
				boolean allowCredentials, 
				int maxAge) {
			Arrays.sort(this.allowedOrigins = allowedOrigins);
			Arrays.sort(this.allowedMethods = allowedMethods);
			this.headerExpressions = headerExpressions;
			this.allowCredentials = allowCredentials;
			this.maxAge = maxAge;
		}
		
		@CrossOrigin
		@RequestMapping(method=RequestMethod.OPTIONS, headers={"Origin", "Access-Control-Request-Method"})
		private @ResponseBody ResponseEntity<Void> universalCorsPreflightHandler(
				@RequestHeader("Origin") String requestedOrigin,
				@RequestHeader("Access-Control-Request-Method") RequestMethod requestedMethod,
				@RequestHeader(value="Access-Control-Request-Headers", required=false) String[] requestedHeaders) {
			
			if (Arrays.binarySearch(allowedOrigins, requestedOrigin) < 0
				&& Arrays.binarySearch(allowedOrigins, "*") < 0) {
				return new ResponseEntity<Void>(HttpStatus.FORBIDDEN);
			}
			
			if (Arrays.binarySearch(allowedMethods, requestedMethod) < 0) {
				return new ResponseEntity<Void>(HttpStatus.FORBIDDEN);
			}

			//check the header conditions.  Note, this assumes extraneous headers from the user-agent are valid.
			if (requestedHeaders != null) Arrays.sort(requestedHeaders);
			for (NameValueExpression<String> headerExpression : headerExpressions) {
				if (headerExpression.isNegated()) {
					if (requestedHeaders != null && Arrays.binarySearch(requestedHeaders, headerExpression.getName()) >= 0) {
						//user-agent is requesting to send a header that is negated by the primary handler
						return new ResponseEntity<Void>(HttpStatus.FORBIDDEN);
					}   //else user-agent is not sending the negated header, thus no issues.
				}
				else {
					if (requestedHeaders == null || Arrays.binarySearch(requestedHeaders, headerExpression.getName()) < 0) {
						//user-agent is missing (not requested) a header that is required by the primary handler
						return new ResponseEntity<Void>(HttpStatus.FORBIDDEN);
					}   //else user-agent is sending the required header, thus no issues.
				}
			}
			
			HttpHeaders responseHeaders = new HttpHeaders();
			//CORS Required PREFLIGHT Only - Tells browser which header values may be transmitted.
			if (requestedHeaders != null) responseHeaders.set("Access-Control-Allow-Headers", StringUtils.arrayToDelimitedString(requestedHeaders, ", "));
			//CORS Required PREFLIGHT Only - Tells browser which http methods are allowed.
			responseHeaders.set("Access-Control-Allow-Methods", StringUtils.arrayToDelimitedString(allowedMethods, ", "));
			//CORS Optional PREFLIGHT Only - Tells browser it can cache the OPTIONS response for indicated seconds, thus avoiding double requests to the server for previously pre-flight'd URIs. 
			if (maxAge > 0) responseHeaders.set("Access-Control-Max-Age", "3600");
			//CORS Optional - (in PREFLIGHT response) tells Browser it should send cookies.  
			if (allowCredentials) responseHeaders.set("Access-Control-Allow-Credentials", "true");
			return new ResponseEntity<Void>(responseHeaders, HttpStatus.NO_CONTENT);
		}
	}
}

The above is not the most elegant code, but it gets the job done. Obviously, anyone wishing to take this route should refactor the code and at minimum separate the handler from mapper.

The above code respects header expressions properly. If the primary handler mapping declares header="!Foo", then the CORS OPTIONS handler will enforce that Foo is not in the accept headers list. Conversely, if the primary handler requires a header, then the OPTIONS handler will enforce its presence in the accept header list. Finally (IMPORTANT), the OPTIONS handler will implicitly ALLOW extraneous headers not otherwise mentioned by the primary handler. That may need to be an additional configurable behavior of the OPTIONS handler.

Also, note that the @CrossOrigin annotation has its own header attribute. That attribute defines which RESPONSE headers are exposed to the client. It is a completely different set of headers than those specified in the pre-flight allowed headers list.

In order to use this code you will need to replace the traditional RequestMappingHandlerMapping bean with the above implementation. It is defined in the Web MVC Configuration, which is easily done via java config:

@Configuration
public class CusstomWebConfig extends WebMvcConfigurationSupport {
	
	@Bean
	@Override
	public RequestMappingHandlerMapping requestMappingHandlerMapping() {
		try {
			RequestMappingHandlerMapping handlerMapping = new CrossOriginRequestMappingHandlerMapping();
			handlerMapping.setOrder(0);
			handlerMapping.setInterceptors(getInterceptors());
			handlerMapping.setContentNegotiationManager(mvcContentNegotiationManager());
			return handlerMapping;
		} catch (NoSuchMethodException | SecurityException e) {
			throw new IllegalStateException("Failed to create CORS Preflight Request Mapping Handler.", e);
		}
	}	
}

Now, create the @CrossOrigin annotation and add it to any @RequestMapping annotated handler to make it CORS compliant:

@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface CrossOrigin {

	String[] origin() default {"*"};
	String[] header() default {};
	boolean allowCredentials() default false;
	int maxAge() default 0;
	
}

Finally, don't forget to configure the dispatcher servlet to pass through options request in web.xml:

<servlet>
    <servlet-name>dispatcher</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
 <init-param>
      <param-name>dispatchOptionsRequest</param-name>
      <param-value>true</param-value>
 </init-param>
</servlet>

Parting Thoughts:

  • Security (Spring-Security in my case) is a bit of a monkey wrench as the browser will not include headers (like Authorization) on the OPTIONS request, and the security filter rejects the call before the CORS request method handler has a chance to reply that it's ok to send such headers. This is best addressed (IMHO) by adjusting your security intercept patterns to include the request method as part of the definition.
  • I was focused on handling pre-flight requests, but I think this handles simple CORS requests as well.
  • This approach assumes annotation based request mapping. If you have old school bean name based mapping or any other non-annotation based mapping, then this will need to be rewritten for that mapping methodology.
  • This code handles writing the headers in the response... via some major hackery. This part of the code should be done differently, but that requires some changes to RequestMappingHandlerAdapter which I don't have access to.

Oh, one last class (used to do intercept and inject data into @ResponseBody headers):

public abstract class HandlerMethodReturnValueHandlerInterceptor implements HandlerMethodReturnValueHandler {

	private HandlerMethodReturnValueHandler delegate;
	
	public HandlerMethodReturnValueHandlerInterceptor(HandlerMethodReturnValueHandler delegate) {
		this.delegate = delegate;
	}
	
	@Override
	public boolean supportsReturnType(MethodParameter returnType) {
		return delegate.supportsReturnType(returnType);
	}

	@Override
	public void handleReturnValue(Object returnValue,
			MethodParameter returnType, ModelAndViewContainer mavContainer,
			NativeWebRequest webRequest) throws Exception {
		preHandle(returnValue, returnType, mavContainer, webRequest);
		delegate.handleReturnValue(returnValue, returnType, mavContainer, webRequest);
		postHandle(returnValue, returnType, mavContainer, webRequest);
	}

	protected void preHandle(Object returnValue, MethodParameter returnType,
		ModelAndViewContainer mavContainer, NativeWebRequest webRequest) {
	}

	protected void postHandle(Object returnValue, MethodParameter returnType,
		ModelAndViewContainer mavContainer, NativeWebRequest webRequest) {
	}

}

@spring-projects-issues
Copy link
Collaborator Author

Rossen Stoyanchev commented

Thanks for sharing this approach in great detail. Adding fine-grained support on the level of a specific method is worth considering. I'm curious whether you tried using a Filter like the ones Jetty and Tomcat provide for a more centralized configuration vs adding metadata to individual @RequestMapping methods?

@spring-projects-issues
Copy link
Collaborator Author

Russell Allen commented

I considered the filter approach, and it certainly has its benefits. For many, the ability to 'turn on' cross origin requests in on place across all endpoints in a server and to manager the configuration in a single location is a great solution.

The down side is that it provides no fine grain control without the risk of having an ever growing set of specific configurations... configurations that are ultimately co-variant to the handler method they're managing access to. If the developer wants to constrain CORS requests on a per handler basis, then it makes sense to do that at the actual handler via an annotation.

Ultimately, I suspect I may be in the minority needing/wanting that granular control.

Another factor that might influence a global filter vs annotated handler debate is: What was the intent of the CORS specification? More specifically, is it ok, even expected, that a CORS pre-flight OPTIONS request can respond successfully (HTTP 2XX) and the subsequent actual request still be rejected because it's missing a required header or contains an unacceptable header?

The filter approach doesn't, in my opinion, provide a precise reply to the pre-flight OPTIONS request. As a result, many clients will receive successful pre-flight responses only to have the actual request rejected. Perhaps this is ok though, since the rejection of the actual request will likely contain better information about why it failed. If the pre-flight request was rejected, the user-agent will prevent the client from seeing any response details.

As you can see, there's arguments for either approach. I'm curious what others think on the matter. :)

@spring-projects-issues
Copy link
Collaborator Author

Rossen Stoyanchev commented

Current links to Jetty's CrossOriginFilter (tests, doc) and Tomcat's CorsFilter (tests, doc).

@spring-projects-issues
Copy link
Collaborator Author

spring-projects-issues commented Feb 18, 2014

Rossen Stoyanchev commented

The use case under under #16063 is a good one to validate against. For once the SockJsService should either build on the support provided here and/or play well with it if enabled. Also it shows that for endpoints that already have built-in CORS support, it should be easy to exclude specific URLs.

@spring-projects-issues
Copy link
Collaborator Author

Russell Allen commented

Sébastien, if you'd like, I can post to github the annotation based CORS support that I described in the prior comment. Since that post, the code has had some testing and production exposure, and there's been a few bug fixes as a result.

If that's not the approach you're taking, then no worries! :)

@spring-projects-issues
Copy link
Collaborator Author

Sébastien Deleuze commented

Russell Allen Thanks, I am still testing various approaches, so yes I would be interested by your latest version. Did you change something about adding headers for actual requests ?

@spring-projects-issues
Copy link
Collaborator Author

Russell Allen commented

I extracted the CORS support into a repo by itself, here: https://github.com/Russell-Allen/CrossOrigin

This implementation does support custom headers on cross origin requests. The browser will pass the header key that the client is attempting to add to the primary request onto the pre-flight request's Access-Control-Request-Headers header. This implementation compares the names found in the pre-flight Access-Control-Request-Headers header to and headers conditions on the request mapping. Internally, it uses Spring's HeadersRequestCondition from the RequestMappingInfo of the primary request handler. Thus, any header that the primary request handler expects will be accepted by the pre-flight handler.

Response headers are also supported. The @CrossOrigin annotation has a header attribute (String[]) that allows the developer to list header names that should be exposed to the client of a cross origin request. The values listed in the CrossOrigin.header list are placed into the Access-Control-Expose-Headers of the primary response.

Let me know if you have any questions.

@spring-projects-issues
Copy link
Collaborator Author

Pratik Parikh commented

Is it possible to add test cases to Allen's Implementation and explain how this could work with Spring Data Rest.

@spring-projects-issues
Copy link
Collaborator Author

Eric Rath commented

There's another use-case that would benefit from CORS support: static assets served via the mvc:resources element. We have a Spring 4.0 webapp that serves CSS, JS, and PNGs with a mvc:resources element (e.g. http://www.example.com/assets/fonts/myfont.woff). The app also has a configurable CDN host used when generating the URLs for those assets (e.g. http://myapp.cloudfront.net/assets/styles/style.css). The CloudFront distribution was configured to use our app as its custom origin; when CF receives request for http://myapp.cloudfront.net/assets/fonts/myfont.woff, it retrieves from http://www.example.com/assets/fonts/myfont.woff, caches the result, and serves response. We configured the CF distribution to pass the Origin header through, but still needed to modify our app -- adding a CORS interceptor -- to add the appropriate Access-Control-Allowed-Origins header.

I looked at Russell Allen's annotation-driven solution; could we also add a child element to mvc:resources that defined list of allowed origins? This would add CORS support to static assets.

@spring-projects-issues
Copy link
Collaborator Author

Sébastien Deleuze commented

I have merged today a first CORS implementation in master (see this commit), and I would be interested by your feedback.

As proposed by Russell Allen, a new annotation @CrossOrigin allows to enable CORS support on Controller type or method level.

@RestController
public class SampleController {

	@CrossOrigin
	@RequestMapping("/foo")
	public String foo() {
		// ...
	}
}

Various @CrossOrigin attributes allow to customize the CORS configuration.

@RestController
public class SampleController {

	@CrossOrigin(origin = { "http://site1.com", "http://site2.com" },
				 allowedHeaders = { "header1", "header2" },
				 exposedHeaders = { "header1", "header2" },
				 method = RequestMethod.DELETE,
				 maxAge = 123, allowCredentials = "true")
	@RequestMapping(value = "/foo", method = { RequestMethod.GET, RequestMethod.POST} )
	public String foo() {
		// ...
	}
}

A CorsConfigurationSource interface can be implemented by HTTP request handlers that want to support CORS by providing a CorsConfiguration that will be detected at AbstractHandlerMapping level. See for example ResourceHttpRequestHandler that implements this interface.

Global CORS configuration is not yet implemented, and may eventually be supported through ControllerAdvice (with type level @CrossOrigin annotated class or class implementing CorsConfigurationSource), or with XML namespace and JavaConfig.

I would be interested by your feedback, so don't hesitate to give a try to our latest 4.2.0.BUILD-SNAPSHOT build and post your feedback here.

@spring-projects-issues
Copy link
Collaborator Author

Hantsy Bai commented

The fine-grained config is good, but for me, I would like use CORS config globally.

  1. I prefer InterceptorRegistry or XXXConfigurer like fluent API to enable CORS config for certain URI in web configuration, eg.

cors.enable();
cors.addPathPattern("/api/*").allowMethods().allowHeaders()...

  1. Add Spring related headers to allowHeaders by default, such CSRF-TOKEN(Spring security) , x-auth-token(Spring Session) etc.

@spring-projects-issues
Copy link
Collaborator Author

Rossen Stoyanchev commented

Hantsy Bai thanks for the comment.

Note that with the current changes we are automatically "CORS aware". For example the DispatcherServlet lets preflight requests (HTTP OPTIONS) through to be handled because CORS is now a built-in feature of HandlerMapping's with AbstractHandlerMapping as the overall driver and each sub-class further responsible for identifying what CorsConfiguration applies to the mapped handler which could be from @CrossOrigin for controllers or based on whether a handler implements CorsConfigurationSource.

In short nothing to enable. Simply start adding @CrossOrigin or else cross-origin requests will not be allowed.

The addition of a CORS configuration option that's externalized (not in the controller or handler) makes sense. It would be complementary to any configuration a controller or handler might already expose. In other words a union of the two. That way you can enable commonly used headers globally as you pointed out.

For the implementation a Servlet Filter is best positioned for something like this as a single component with central control. A HandlerInterceptor is not ideal. It depends on the selection of a handler and doesn't get invoked until the handler is invoked, which creates issues for preflight requests. We do have a CorsProcessor that can be injected into a HandlerMapping. It might the best place to start by adding URL-mapped CORS configuration and making it a CorsConfigurationSource.

@spring-projects-issues
Copy link
Collaborator Author

Sébastien Deleuze commented

I have just pushed to master global CORS configuration capabilities support, see this commit for more details.

@spring-projects-issues
Copy link
Collaborator Author

Hantsy Bai commented

Thanks for resolving this issue.

But I am a little confused about the naming. eg.

CorsConfiguration
CorsProcessor
CrossOriginConfigurer
CrossOriginRegistration
configureCrossOrigin( method in DelegatingWebMvcConfiguration and WebMvcConfigurer)
~.web.cors(package )

For me , I perferred use "Cors" in above.

Why not use "Cors" or "CrossOrigin" in these items, or there are some specific reason for this purpose?

@spring-projects-issues
Copy link
Collaborator Author

Sébastien Deleuze commented

Since we use @CrossOrigin to annotate CORS enabled handler methods, I chose for the moment CrossOriginConfigurer, CrossOriginRegistration and configureCrossOrigin for global configuration API. More low level CORS stuff are still using Cors prefix as you noticed.

But this is something that we may revisit, based on feedbacks and discussion in Spring Framework team.
So thanks for your feedback, I will keep your informed.

@spring-projects-issues
Copy link
Collaborator Author

Sébastien Deleuze commented

I have renamed CrossOriginConfigurer to CorsConfigurer, CrossOriginRegistration to CorsRegistration and configureCrossOrigin to configureCors.

@spring-projects-issues
Copy link
Collaborator Author

Fabian Wüthrich commented

This new feature is great and I want to implement this in my Spring Boot / AngularJS application. All request works fine but I can't logout my user because the OPTIONS-Request to /logout is handled by Spring Security. Should I open an issue in Spring Security project or is it possible to attach CORS-Headers in LogoutSuccessHandler?

@spring-projects-issues
Copy link
Collaborator Author

Sébastien Deleuze commented

Fabian Wüthrich Could you please create a StackOverflow question for that and add a comment here with the link? I will answer you on SO.

@spring-projects-issues
Copy link
Collaborator Author

@spring-projects-issues spring-projects-issues added type: enhancement A general enhancement has: votes-jira Issues migrated from JIRA with more than 10 votes at the time of import in: web Issues in web modules (web, webmvc, webflux, websocket) labels Jan 11, 2019
@spring-projects-issues spring-projects-issues added this to the 4.2 RC1 milestone Jan 11, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
has: votes-jira Issues migrated from JIRA with more than 10 votes at the time of import in: web Issues in web modules (web, webmvc, webflux, websocket) type: enhancement A general enhancement
Projects
None yet
Development

No branches or pull requests

2 participants