Skip to content

Commit 8852a5e

Browse files
committed
Support for placeholders in @crossorigin attributes
Issue: SPR-14010
1 parent b4de66f commit 8852a5e

File tree

2 files changed

+73
-37
lines changed

2 files changed

+73
-37
lines changed

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

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2015 the original author or authors.
2+
* Copyright 2002-2016 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -109,7 +109,7 @@ public void setContentNegotiationManager(ContentNegotiationManager contentNegoti
109109

110110
@Override
111111
public void setEmbeddedValueResolver(StringValueResolver resolver) {
112-
this.embeddedValueResolver = resolver;
112+
this.embeddedValueResolver = resolver;
113113
}
114114

115115
@Override
@@ -314,33 +314,37 @@ private void updateCorsConfig(CorsConfiguration config, CrossOrigin annotation)
314314
return;
315315
}
316316
for (String origin : annotation.origins()) {
317-
config.addAllowedOrigin(origin);
317+
config.addAllowedOrigin(resolveCorsAnnotationValue(origin));
318318
}
319319
for (RequestMethod method : annotation.methods()) {
320320
config.addAllowedMethod(method.name());
321321
}
322322
for (String header : annotation.allowedHeaders()) {
323-
config.addAllowedHeader(header);
323+
config.addAllowedHeader(resolveCorsAnnotationValue(header));
324324
}
325325
for (String header : annotation.exposedHeaders()) {
326-
config.addExposedHeader(header);
326+
config.addExposedHeader(resolveCorsAnnotationValue(header));
327327
}
328328

329-
String allowCredentials = annotation.allowCredentials();
329+
String allowCredentials = resolveCorsAnnotationValue(annotation.allowCredentials());
330330
if ("true".equalsIgnoreCase(allowCredentials)) {
331331
config.setAllowCredentials(true);
332332
}
333333
else if ("false".equalsIgnoreCase(allowCredentials)) {
334334
config.setAllowCredentials(false);
335335
}
336336
else if (!allowCredentials.isEmpty()) {
337-
throw new IllegalStateException("@CrossOrigin's allowCredentials value must be \"true\", \"false\", "
338-
+ "or an empty string (\"\"); current value is [" + allowCredentials + "].");
337+
throw new IllegalStateException("@CrossOrigin's allowCredentials value must be \"true\", \"false\", " +
338+
"or an empty string (\"\"): current value is [" + allowCredentials + "]");
339339
}
340340

341341
if (annotation.maxAge() >= 0 && config.getMaxAge() == null) {
342342
config.setMaxAge(annotation.maxAge());
343343
}
344344
}
345345

346+
private String resolveCorsAnnotationValue(String value) {
347+
return (this.embeddedValueResolver != null ? this.embeddedValueResolver.resolveStringValue(value) : value);
348+
}
349+
346350
}

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

Lines changed: 61 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2015 the original author or authors.
2+
* Copyright 2002-2016 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -22,15 +22,18 @@
2222
import java.lang.annotation.Target;
2323
import java.lang.reflect.Method;
2424
import java.util.Arrays;
25+
import java.util.Properties;
2526

2627
import org.junit.Before;
2728
import org.junit.Rule;
2829
import org.junit.Test;
2930
import org.junit.rules.ExpectedException;
3031

3132
import org.springframework.beans.DirectFieldAccessor;
33+
import org.springframework.context.support.PropertySourcesPlaceholderConfigurer;
3234
import org.springframework.core.annotation.AnnotatedElementUtils;
3335
import org.springframework.core.annotation.AnnotationUtils;
36+
import org.springframework.core.env.PropertiesPropertySource;
3437
import org.springframework.http.HttpHeaders;
3538
import org.springframework.mock.web.test.MockHttpServletRequest;
3639
import org.springframework.stereotype.Controller;
@@ -72,9 +75,15 @@ public class CrossOriginTests {
7275

7376
@Before
7477
public void setUp() {
78+
StaticWebApplicationContext wac = new StaticWebApplicationContext();
79+
Properties props = new Properties();
80+
props.setProperty("myOrigin", "http://example.com");
81+
wac.getEnvironment().getPropertySources().addFirst(new PropertiesPropertySource("ps", props));
82+
wac.registerSingleton("ppc", PropertySourcesPlaceholderConfigurer.class);
83+
wac.refresh();
84+
7585
this.handlerMapping.setRemoveSemicolonContent(false);
76-
this.handlerMapping.setApplicationContext(new StaticWebApplicationContext());
77-
this.handlerMapping.afterPropertiesSet();
86+
wac.getAutowireCapableBeanFactory().initializeBean(this.handlerMapping, "hm");
7887

7988
this.request.setMethod("GET");
8089
this.request.addHeader(HttpHeaders.ORIGIN, "http://domain.com/");
@@ -112,10 +121,10 @@ public void defaultAnnotation() throws Exception {
112121
HandlerExecutionChain chain = this.handlerMapping.getHandler(request);
113122
CorsConfiguration config = getCorsConfiguration(chain, false);
114123
assertNotNull(config);
115-
assertArrayEquals(new String[]{"GET"}, config.getAllowedMethods().toArray());
116-
assertArrayEquals(new String[]{"*"}, config.getAllowedOrigins().toArray());
124+
assertArrayEquals(new String[] {"GET"}, config.getAllowedMethods().toArray());
125+
assertArrayEquals(new String[] {"*"}, config.getAllowedOrigins().toArray());
117126
assertTrue(config.getAllowCredentials());
118-
assertArrayEquals(new String[]{"*"}, config.getAllowedHeaders().toArray());
127+
assertArrayEquals(new String[] {"*"}, config.getAllowedHeaders().toArray());
119128
assertTrue(CollectionUtils.isEmpty(config.getExposedHeaders()));
120129
assertEquals(new Long(1800), config.getMaxAge());
121130
}
@@ -127,10 +136,10 @@ public void customized() throws Exception {
127136
HandlerExecutionChain chain = this.handlerMapping.getHandler(request);
128137
CorsConfiguration config = getCorsConfiguration(chain, false);
129138
assertNotNull(config);
130-
assertArrayEquals(new String[]{"DELETE"}, config.getAllowedMethods().toArray());
131-
assertArrayEquals(new String[]{"http://site1.com", "http://site2.com"}, config.getAllowedOrigins().toArray());
132-
assertArrayEquals(new String[]{"header1", "header2"}, config.getAllowedHeaders().toArray());
133-
assertArrayEquals(new String[]{"header3", "header4"}, config.getExposedHeaders().toArray());
139+
assertArrayEquals(new String[] {"DELETE"}, config.getAllowedMethods().toArray());
140+
assertArrayEquals(new String[] {"http://site1.com", "http://site2.com"}, config.getAllowedOrigins().toArray());
141+
assertArrayEquals(new String[] {"header1", "header2"}, config.getAllowedHeaders().toArray());
142+
assertArrayEquals(new String[] {"header3", "header4"}, config.getExposedHeaders().toArray());
134143
assertEquals(new Long(123), config.getMaxAge());
135144
assertFalse(config.getAllowCredentials());
136145
}
@@ -146,6 +155,17 @@ public void customOriginDefinedViaValueAttribute() throws Exception {
146155
assertTrue(config.getAllowCredentials());
147156
}
148157

158+
@Test
159+
public void customOriginDefinedViaPlaceholder() throws Exception {
160+
this.handlerMapping.registerHandler(new MethodLevelController());
161+
this.request.setRequestURI("/someOrigin");
162+
HandlerExecutionChain chain = this.handlerMapping.getHandler(request);
163+
CorsConfiguration config = getCorsConfiguration(chain, false);
164+
assertNotNull(config);
165+
assertEquals(Arrays.asList("http://example.com"), config.getAllowedOrigins());
166+
assertTrue(config.getAllowCredentials());
167+
}
168+
149169
@Test
150170
public void bogusAllowCredentialsValue() throws Exception {
151171
exception.expect(IllegalStateException.class);
@@ -162,24 +182,24 @@ public void classLevel() throws Exception {
162182
HandlerExecutionChain chain = this.handlerMapping.getHandler(request);
163183
CorsConfiguration config = getCorsConfiguration(chain, false);
164184
assertNotNull(config);
165-
assertArrayEquals(new String[]{"GET"}, config.getAllowedMethods().toArray());
166-
assertArrayEquals(new String[]{"*"}, config.getAllowedOrigins().toArray());
185+
assertArrayEquals(new String[] {"GET"}, config.getAllowedMethods().toArray());
186+
assertArrayEquals(new String[] {"*"}, config.getAllowedOrigins().toArray());
167187
assertFalse(config.getAllowCredentials());
168188

169189
this.request.setRequestURI("/bar");
170190
chain = this.handlerMapping.getHandler(request);
171191
config = getCorsConfiguration(chain, false);
172192
assertNotNull(config);
173-
assertArrayEquals(new String[]{"GET"}, config.getAllowedMethods().toArray());
174-
assertArrayEquals(new String[]{"*"}, config.getAllowedOrigins().toArray());
193+
assertArrayEquals(new String[] {"GET"}, config.getAllowedMethods().toArray());
194+
assertArrayEquals(new String[] {"*"}, config.getAllowedOrigins().toArray());
175195
assertFalse(config.getAllowCredentials());
176196

177197
this.request.setRequestURI("/baz");
178198
chain = this.handlerMapping.getHandler(request);
179199
config = getCorsConfiguration(chain, false);
180200
assertNotNull(config);
181-
assertArrayEquals(new String[]{"GET"}, config.getAllowedMethods().toArray());
182-
assertArrayEquals(new String[]{"*"}, config.getAllowedOrigins().toArray());
201+
assertArrayEquals(new String[] {"GET"}, config.getAllowedMethods().toArray());
202+
assertArrayEquals(new String[] {"*"}, config.getAllowedOrigins().toArray());
183203
assertTrue(config.getAllowCredentials());
184204
}
185205

@@ -191,8 +211,8 @@ public void classLevelComposedAnnotation() throws Exception {
191211
HandlerExecutionChain chain = this.handlerMapping.getHandler(request);
192212
CorsConfiguration config = getCorsConfiguration(chain, false);
193213
assertNotNull(config);
194-
assertArrayEquals(new String[]{"GET"}, config.getAllowedMethods().toArray());
195-
assertArrayEquals(new String[]{"http://foo.com"}, config.getAllowedOrigins().toArray());
214+
assertArrayEquals(new String[] {"GET"}, config.getAllowedMethods().toArray());
215+
assertArrayEquals(new String[] {"http://foo.com"}, config.getAllowedOrigins().toArray());
196216
assertTrue(config.getAllowCredentials());
197217
}
198218

@@ -204,8 +224,8 @@ public void methodLevelComposedAnnotation() throws Exception {
204224
HandlerExecutionChain chain = this.handlerMapping.getHandler(request);
205225
CorsConfiguration config = getCorsConfiguration(chain, false);
206226
assertNotNull(config);
207-
assertArrayEquals(new String[]{"GET"}, config.getAllowedMethods().toArray());
208-
assertArrayEquals(new String[]{"http://foo.com"}, config.getAllowedOrigins().toArray());
227+
assertArrayEquals(new String[] {"GET"}, config.getAllowedMethods().toArray());
228+
assertArrayEquals(new String[] {"http://foo.com"}, config.getAllowedOrigins().toArray());
209229
assertTrue(config.getAllowCredentials());
210230
}
211231

@@ -218,10 +238,10 @@ public void preFlightRequest() throws Exception {
218238
HandlerExecutionChain chain = this.handlerMapping.getHandler(request);
219239
CorsConfiguration config = getCorsConfiguration(chain, true);
220240
assertNotNull(config);
221-
assertArrayEquals(new String[]{"GET"}, config.getAllowedMethods().toArray());
222-
assertArrayEquals(new String[]{"*"}, config.getAllowedOrigins().toArray());
241+
assertArrayEquals(new String[] {"GET"}, config.getAllowedMethods().toArray());
242+
assertArrayEquals(new String[] {"*"}, config.getAllowedOrigins().toArray());
223243
assertTrue(config.getAllowCredentials());
224-
assertArrayEquals(new String[]{"*"}, config.getAllowedHeaders().toArray());
244+
assertArrayEquals(new String[] {"*"}, config.getAllowedHeaders().toArray());
225245
assertTrue(CollectionUtils.isEmpty(config.getExposedHeaders()));
226246
assertEquals(new Long(1800), config.getMaxAge());
227247
}
@@ -236,9 +256,9 @@ public void ambiguousHeaderPreFlightRequest() throws Exception {
236256
HandlerExecutionChain chain = this.handlerMapping.getHandler(request);
237257
CorsConfiguration config = getCorsConfiguration(chain, true);
238258
assertNotNull(config);
239-
assertArrayEquals(new String[]{"*"}, config.getAllowedMethods().toArray());
240-
assertArrayEquals(new String[]{"*"}, config.getAllowedOrigins().toArray());
241-
assertArrayEquals(new String[]{"*"}, config.getAllowedHeaders().toArray());
259+
assertArrayEquals(new String[] {"*"}, config.getAllowedMethods().toArray());
260+
assertArrayEquals(new String[] {"*"}, config.getAllowedOrigins().toArray());
261+
assertArrayEquals(new String[] {"*"}, config.getAllowedHeaders().toArray());
242262
assertTrue(config.getAllowCredentials());
243263
assertTrue(CollectionUtils.isEmpty(config.getExposedHeaders()));
244264
assertNull(config.getMaxAge());
@@ -253,9 +273,9 @@ public void ambiguousProducesPreFlightRequest() throws Exception {
253273
HandlerExecutionChain chain = this.handlerMapping.getHandler(request);
254274
CorsConfiguration config = getCorsConfiguration(chain, true);
255275
assertNotNull(config);
256-
assertArrayEquals(new String[]{"*"}, config.getAllowedMethods().toArray());
257-
assertArrayEquals(new String[]{"*"}, config.getAllowedOrigins().toArray());
258-
assertArrayEquals(new String[]{"*"}, config.getAllowedHeaders().toArray());
276+
assertArrayEquals(new String[] {"*"}, config.getAllowedMethods().toArray());
277+
assertArrayEquals(new String[] {"*"}, config.getAllowedOrigins().toArray());
278+
assertArrayEquals(new String[] {"*"}, config.getAllowedHeaders().toArray());
259279
assertTrue(config.getAllowCredentials());
260280
assertTrue(CollectionUtils.isEmpty(config.getExposedHeaders()));
261281
assertNull(config.getMaxAge());
@@ -343,8 +363,14 @@ public void customized() {
343363
@RequestMapping("/customOrigin")
344364
public void customOriginDefinedViaValueAttribute() {
345365
}
366+
367+
@CrossOrigin("${myOrigin}")
368+
@RequestMapping("/someOrigin")
369+
public void customOriginDefinedViaPlaceholder() {
370+
}
346371
}
347372

373+
348374
@Controller
349375
private static class MethodLevelControllerWithBogusAllowCredentialsValue {
350376

@@ -354,6 +380,7 @@ public void bogusAllowCredentialsValue() {
354380
}
355381
}
356382

383+
357384
@Controller
358385
@CrossOrigin(allowCredentials = "false")
359386
private static class ClassLevelController {
@@ -374,14 +401,18 @@ public void baz() {
374401

375402
}
376403

404+
377405
@Target({ElementType.METHOD, ElementType.TYPE})
378406
@Retention(RetentionPolicy.RUNTIME)
379407
@CrossOrigin
380408
private @interface ComposedCrossOrigin {
409+
381410
String[] origins() default {};
411+
382412
String allowCredentials() default "";
383413
}
384414

415+
385416
@Controller
386417
@ComposedCrossOrigin(origins = "http://foo.com", allowCredentials = "true")
387418
private static class ClassLevelMappingWithComposedAnnotation {
@@ -401,6 +432,7 @@ public void foo() {
401432
}
402433
}
403434

435+
404436
private static class TestRequestMappingInfoHandlerMapping extends RequestMappingHandlerMapping {
405437

406438
public void registerHandler(Object handler) {

0 commit comments

Comments
 (0)