Browse Source

Support for placeholders in @CrossOrigin attributes

Issue: SPR-14010
pull/991/merge
Juergen Hoeller 10 years ago
parent
commit
8852a5e178
  1. 20
      spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/RequestMappingHandlerMapping.java
  2. 90
      spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/method/annotation/CrossOriginTests.java

20
spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/RequestMappingHandlerMapping.java

@ -1,5 +1,5 @@ @@ -1,5 +1,5 @@
/*
* Copyright 2002-2015 the original author or authors.
* Copyright 2002-2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -109,7 +109,7 @@ public class RequestMappingHandlerMapping extends RequestMappingInfoHandlerMappi @@ -109,7 +109,7 @@ public class RequestMappingHandlerMapping extends RequestMappingInfoHandlerMappi
@Override
public void setEmbeddedValueResolver(StringValueResolver resolver) {
this.embeddedValueResolver = resolver;
this.embeddedValueResolver = resolver;
}
@Override
@ -314,19 +314,19 @@ public class RequestMappingHandlerMapping extends RequestMappingInfoHandlerMappi @@ -314,19 +314,19 @@ public class RequestMappingHandlerMapping extends RequestMappingInfoHandlerMappi
return;
}
for (String origin : annotation.origins()) {
config.addAllowedOrigin(origin);
config.addAllowedOrigin(resolveCorsAnnotationValue(origin));
}
for (RequestMethod method : annotation.methods()) {
config.addAllowedMethod(method.name());
}
for (String header : annotation.allowedHeaders()) {
config.addAllowedHeader(header);
config.addAllowedHeader(resolveCorsAnnotationValue(header));
}
for (String header : annotation.exposedHeaders()) {
config.addExposedHeader(header);
config.addExposedHeader(resolveCorsAnnotationValue(header));
}
String allowCredentials = annotation.allowCredentials();
String allowCredentials = resolveCorsAnnotationValue(annotation.allowCredentials());
if ("true".equalsIgnoreCase(allowCredentials)) {
config.setAllowCredentials(true);
}
@ -334,8 +334,8 @@ public class RequestMappingHandlerMapping extends RequestMappingInfoHandlerMappi @@ -334,8 +334,8 @@ public class RequestMappingHandlerMapping extends RequestMappingInfoHandlerMappi
config.setAllowCredentials(false);
}
else if (!allowCredentials.isEmpty()) {
throw new IllegalStateException("@CrossOrigin's allowCredentials value must be \"true\", \"false\", "
+ "or an empty string (\"\"); current value is [" + allowCredentials + "].");
throw new IllegalStateException("@CrossOrigin's allowCredentials value must be \"true\", \"false\", " +
"or an empty string (\"\"): current value is [" + allowCredentials + "]");
}
if (annotation.maxAge() >= 0 && config.getMaxAge() == null) {
@ -343,4 +343,8 @@ public class RequestMappingHandlerMapping extends RequestMappingInfoHandlerMappi @@ -343,4 +343,8 @@ public class RequestMappingHandlerMapping extends RequestMappingInfoHandlerMappi
}
}
private String resolveCorsAnnotationValue(String value) {
return (this.embeddedValueResolver != null ? this.embeddedValueResolver.resolveStringValue(value) : value);
}
}

90
spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/method/annotation/CrossOriginTests.java

@ -1,5 +1,5 @@ @@ -1,5 +1,5 @@
/*
* Copyright 2002-2015 the original author or authors.
* Copyright 2002-2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -22,6 +22,7 @@ import java.lang.annotation.RetentionPolicy; @@ -22,6 +22,7 @@ import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.Properties;
import org.junit.Before;
import org.junit.Rule;
@ -29,8 +30,10 @@ import org.junit.Test; @@ -29,8 +30,10 @@ import org.junit.Test;
import org.junit.rules.ExpectedException;
import org.springframework.beans.DirectFieldAccessor;
import org.springframework.context.support.PropertySourcesPlaceholderConfigurer;
import org.springframework.core.annotation.AnnotatedElementUtils;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.core.env.PropertiesPropertySource;
import org.springframework.http.HttpHeaders;
import org.springframework.mock.web.test.MockHttpServletRequest;
import org.springframework.stereotype.Controller;
@ -72,9 +75,15 @@ public class CrossOriginTests { @@ -72,9 +75,15 @@ public class CrossOriginTests {
@Before
public void setUp() {
StaticWebApplicationContext wac = new StaticWebApplicationContext();
Properties props = new Properties();
props.setProperty("myOrigin", "http://example.com");
wac.getEnvironment().getPropertySources().addFirst(new PropertiesPropertySource("ps", props));
wac.registerSingleton("ppc", PropertySourcesPlaceholderConfigurer.class);
wac.refresh();
this.handlerMapping.setRemoveSemicolonContent(false);
this.handlerMapping.setApplicationContext(new StaticWebApplicationContext());
this.handlerMapping.afterPropertiesSet();
wac.getAutowireCapableBeanFactory().initializeBean(this.handlerMapping, "hm");
this.request.setMethod("GET");
this.request.addHeader(HttpHeaders.ORIGIN, "http://domain.com/");
@ -112,10 +121,10 @@ public class CrossOriginTests { @@ -112,10 +121,10 @@ public class CrossOriginTests {
HandlerExecutionChain chain = this.handlerMapping.getHandler(request);
CorsConfiguration config = getCorsConfiguration(chain, false);
assertNotNull(config);
assertArrayEquals(new String[]{"GET"}, config.getAllowedMethods().toArray());
assertArrayEquals(new String[]{"*"}, config.getAllowedOrigins().toArray());
assertArrayEquals(new String[] {"GET"}, config.getAllowedMethods().toArray());
assertArrayEquals(new String[] {"*"}, config.getAllowedOrigins().toArray());
assertTrue(config.getAllowCredentials());
assertArrayEquals(new String[]{"*"}, config.getAllowedHeaders().toArray());
assertArrayEquals(new String[] {"*"}, config.getAllowedHeaders().toArray());
assertTrue(CollectionUtils.isEmpty(config.getExposedHeaders()));
assertEquals(new Long(1800), config.getMaxAge());
}
@ -127,10 +136,10 @@ public class CrossOriginTests { @@ -127,10 +136,10 @@ public class CrossOriginTests {
HandlerExecutionChain chain = this.handlerMapping.getHandler(request);
CorsConfiguration config = getCorsConfiguration(chain, false);
assertNotNull(config);
assertArrayEquals(new String[]{"DELETE"}, config.getAllowedMethods().toArray());
assertArrayEquals(new String[]{"http://site1.com", "http://site2.com"}, config.getAllowedOrigins().toArray());
assertArrayEquals(new String[]{"header1", "header2"}, config.getAllowedHeaders().toArray());
assertArrayEquals(new String[]{"header3", "header4"}, config.getExposedHeaders().toArray());
assertArrayEquals(new String[] {"DELETE"}, config.getAllowedMethods().toArray());
assertArrayEquals(new String[] {"http://site1.com", "http://site2.com"}, config.getAllowedOrigins().toArray());
assertArrayEquals(new String[] {"header1", "header2"}, config.getAllowedHeaders().toArray());
assertArrayEquals(new String[] {"header3", "header4"}, config.getExposedHeaders().toArray());
assertEquals(new Long(123), config.getMaxAge());
assertFalse(config.getAllowCredentials());
}
@ -146,6 +155,17 @@ public class CrossOriginTests { @@ -146,6 +155,17 @@ public class CrossOriginTests {
assertTrue(config.getAllowCredentials());
}
@Test
public void customOriginDefinedViaPlaceholder() throws Exception {
this.handlerMapping.registerHandler(new MethodLevelController());
this.request.setRequestURI("/someOrigin");
HandlerExecutionChain chain = this.handlerMapping.getHandler(request);
CorsConfiguration config = getCorsConfiguration(chain, false);
assertNotNull(config);
assertEquals(Arrays.asList("http://example.com"), config.getAllowedOrigins());
assertTrue(config.getAllowCredentials());
}
@Test
public void bogusAllowCredentialsValue() throws Exception {
exception.expect(IllegalStateException.class);
@ -162,24 +182,24 @@ public class CrossOriginTests { @@ -162,24 +182,24 @@ public class CrossOriginTests {
HandlerExecutionChain chain = this.handlerMapping.getHandler(request);
CorsConfiguration config = getCorsConfiguration(chain, false);
assertNotNull(config);
assertArrayEquals(new String[]{"GET"}, config.getAllowedMethods().toArray());
assertArrayEquals(new String[]{"*"}, config.getAllowedOrigins().toArray());
assertArrayEquals(new String[] {"GET"}, config.getAllowedMethods().toArray());
assertArrayEquals(new String[] {"*"}, config.getAllowedOrigins().toArray());
assertFalse(config.getAllowCredentials());
this.request.setRequestURI("/bar");
chain = this.handlerMapping.getHandler(request);
config = getCorsConfiguration(chain, false);
assertNotNull(config);
assertArrayEquals(new String[]{"GET"}, config.getAllowedMethods().toArray());
assertArrayEquals(new String[]{"*"}, config.getAllowedOrigins().toArray());
assertArrayEquals(new String[] {"GET"}, config.getAllowedMethods().toArray());
assertArrayEquals(new String[] {"*"}, config.getAllowedOrigins().toArray());
assertFalse(config.getAllowCredentials());
this.request.setRequestURI("/baz");
chain = this.handlerMapping.getHandler(request);
config = getCorsConfiguration(chain, false);
assertNotNull(config);
assertArrayEquals(new String[]{"GET"}, config.getAllowedMethods().toArray());
assertArrayEquals(new String[]{"*"}, config.getAllowedOrigins().toArray());
assertArrayEquals(new String[] {"GET"}, config.getAllowedMethods().toArray());
assertArrayEquals(new String[] {"*"}, config.getAllowedOrigins().toArray());
assertTrue(config.getAllowCredentials());
}
@ -191,8 +211,8 @@ public class CrossOriginTests { @@ -191,8 +211,8 @@ public class CrossOriginTests {
HandlerExecutionChain chain = this.handlerMapping.getHandler(request);
CorsConfiguration config = getCorsConfiguration(chain, false);
assertNotNull(config);
assertArrayEquals(new String[]{"GET"}, config.getAllowedMethods().toArray());
assertArrayEquals(new String[]{"http://foo.com"}, config.getAllowedOrigins().toArray());
assertArrayEquals(new String[] {"GET"}, config.getAllowedMethods().toArray());
assertArrayEquals(new String[] {"http://foo.com"}, config.getAllowedOrigins().toArray());
assertTrue(config.getAllowCredentials());
}
@ -204,8 +224,8 @@ public class CrossOriginTests { @@ -204,8 +224,8 @@ public class CrossOriginTests {
HandlerExecutionChain chain = this.handlerMapping.getHandler(request);
CorsConfiguration config = getCorsConfiguration(chain, false);
assertNotNull(config);
assertArrayEquals(new String[]{"GET"}, config.getAllowedMethods().toArray());
assertArrayEquals(new String[]{"http://foo.com"}, config.getAllowedOrigins().toArray());
assertArrayEquals(new String[] {"GET"}, config.getAllowedMethods().toArray());
assertArrayEquals(new String[] {"http://foo.com"}, config.getAllowedOrigins().toArray());
assertTrue(config.getAllowCredentials());
}
@ -218,10 +238,10 @@ public class CrossOriginTests { @@ -218,10 +238,10 @@ public class CrossOriginTests {
HandlerExecutionChain chain = this.handlerMapping.getHandler(request);
CorsConfiguration config = getCorsConfiguration(chain, true);
assertNotNull(config);
assertArrayEquals(new String[]{"GET"}, config.getAllowedMethods().toArray());
assertArrayEquals(new String[]{"*"}, config.getAllowedOrigins().toArray());
assertArrayEquals(new String[] {"GET"}, config.getAllowedMethods().toArray());
assertArrayEquals(new String[] {"*"}, config.getAllowedOrigins().toArray());
assertTrue(config.getAllowCredentials());
assertArrayEquals(new String[]{"*"}, config.getAllowedHeaders().toArray());
assertArrayEquals(new String[] {"*"}, config.getAllowedHeaders().toArray());
assertTrue(CollectionUtils.isEmpty(config.getExposedHeaders()));
assertEquals(new Long(1800), config.getMaxAge());
}
@ -236,9 +256,9 @@ public class CrossOriginTests { @@ -236,9 +256,9 @@ public class CrossOriginTests {
HandlerExecutionChain chain = this.handlerMapping.getHandler(request);
CorsConfiguration config = getCorsConfiguration(chain, true);
assertNotNull(config);
assertArrayEquals(new String[]{"*"}, config.getAllowedMethods().toArray());
assertArrayEquals(new String[]{"*"}, config.getAllowedOrigins().toArray());
assertArrayEquals(new String[]{"*"}, config.getAllowedHeaders().toArray());
assertArrayEquals(new String[] {"*"}, config.getAllowedMethods().toArray());
assertArrayEquals(new String[] {"*"}, config.getAllowedOrigins().toArray());
assertArrayEquals(new String[] {"*"}, config.getAllowedHeaders().toArray());
assertTrue(config.getAllowCredentials());
assertTrue(CollectionUtils.isEmpty(config.getExposedHeaders()));
assertNull(config.getMaxAge());
@ -253,9 +273,9 @@ public class CrossOriginTests { @@ -253,9 +273,9 @@ public class CrossOriginTests {
HandlerExecutionChain chain = this.handlerMapping.getHandler(request);
CorsConfiguration config = getCorsConfiguration(chain, true);
assertNotNull(config);
assertArrayEquals(new String[]{"*"}, config.getAllowedMethods().toArray());
assertArrayEquals(new String[]{"*"}, config.getAllowedOrigins().toArray());
assertArrayEquals(new String[]{"*"}, config.getAllowedHeaders().toArray());
assertArrayEquals(new String[] {"*"}, config.getAllowedMethods().toArray());
assertArrayEquals(new String[] {"*"}, config.getAllowedOrigins().toArray());
assertArrayEquals(new String[] {"*"}, config.getAllowedHeaders().toArray());
assertTrue(config.getAllowCredentials());
assertTrue(CollectionUtils.isEmpty(config.getExposedHeaders()));
assertNull(config.getMaxAge());
@ -343,8 +363,14 @@ public class CrossOriginTests { @@ -343,8 +363,14 @@ public class CrossOriginTests {
@RequestMapping("/customOrigin")
public void customOriginDefinedViaValueAttribute() {
}
@CrossOrigin("${myOrigin}")
@RequestMapping("/someOrigin")
public void customOriginDefinedViaPlaceholder() {
}
}
@Controller
private static class MethodLevelControllerWithBogusAllowCredentialsValue {
@ -354,6 +380,7 @@ public class CrossOriginTests { @@ -354,6 +380,7 @@ public class CrossOriginTests {
}
}
@Controller
@CrossOrigin(allowCredentials = "false")
private static class ClassLevelController {
@ -374,14 +401,18 @@ public class CrossOriginTests { @@ -374,14 +401,18 @@ public class CrossOriginTests {
}
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@CrossOrigin
private @interface ComposedCrossOrigin {
String[] origins() default {};
String allowCredentials() default "";
}
@Controller
@ComposedCrossOrigin(origins = "http://foo.com", allowCredentials = "true")
private static class ClassLevelMappingWithComposedAnnotation {
@ -401,6 +432,7 @@ public class CrossOriginTests { @@ -401,6 +432,7 @@ public class CrossOriginTests {
}
}
private static class TestRequestMappingInfoHandlerMapping extends RequestMappingHandlerMapping {
public void registerHandler(Object handler) {

Loading…
Cancel
Save