1616
1717package org .springframework .web .servlet .mvc .method .annotation ;
1818
19+ import java .lang .annotation .ElementType ;
20+ import java .lang .annotation .Retention ;
21+ import java .lang .annotation .RetentionPolicy ;
22+ import java .lang .annotation .Target ;
1923import java .lang .reflect .Method ;
2024import java .util .Arrays ;
2125
2529import org .junit .rules .ExpectedException ;
2630
2731import org .springframework .beans .DirectFieldAccessor ;
32+ import org .springframework .core .annotation .AnnotatedElementUtils ;
2833import org .springframework .core .annotation .AnnotationUtils ;
2934import org .springframework .http .HttpHeaders ;
3035import org .springframework .mock .web .test .MockHttpServletRequest ;
5358 *
5459 * @author Sebastien Deleuze
5560 * @author Sam Brannen
61+ * @author Nicolas Labrot
5662 */
5763public class CrossOriginTests {
5864
@@ -79,17 +85,15 @@ public void noAnnotationWithoutOrigin() throws Exception {
7985 this .handlerMapping .registerHandler (new MethodLevelController ());
8086 MockHttpServletRequest request = new MockHttpServletRequest ("GET" , "/no" );
8187 HandlerExecutionChain chain = this .handlerMapping .getHandler (request );
82- CorsConfiguration config = getCorsConfiguration (chain , false );
83- assertNull (config );
88+ assertNull (getCorsConfiguration (chain , false ));
8489 }
8590
8691 @ Test // SPR-12931
8792 public void noAnnotationWithOrigin () throws Exception {
8893 this .handlerMapping .registerHandler (new MethodLevelController ());
8994 this .request .setRequestURI ("/no" );
9095 HandlerExecutionChain chain = this .handlerMapping .getHandler (request );
91- CorsConfiguration config = getCorsConfiguration (chain , false );
92- assertNull (config );
96+ assertNull (getCorsConfiguration (chain , false ));
9397 }
9498
9599 @ Test // SPR-12931
@@ -98,8 +102,7 @@ public void noAnnotationPostWithOrigin() throws Exception {
98102 this .request .setMethod ("POST" );
99103 this .request .setRequestURI ("/no" );
100104 HandlerExecutionChain chain = this .handlerMapping .getHandler (request );
101- CorsConfiguration config = getCorsConfiguration (chain , false );
102- assertNull (config );
105+ assertNull (getCorsConfiguration (chain , false ));
103106 }
104107
105108 @ Test
@@ -180,6 +183,32 @@ public void classLevel() throws Exception {
180183 assertTrue (config .getAllowCredentials ());
181184 }
182185
186+ @ Test // SPR-13468
187+ public void classLevelComposedAnnotation () throws Exception {
188+ this .handlerMapping .registerHandler (new ClassLevelMappingWithComposedAnnotation ());
189+
190+ this .request .setRequestURI ("/foo" );
191+ HandlerExecutionChain chain = this .handlerMapping .getHandler (request );
192+ CorsConfiguration config = getCorsConfiguration (chain , false );
193+ assertNotNull (config );
194+ assertArrayEquals (new String []{"GET" }, config .getAllowedMethods ().toArray ());
195+ assertArrayEquals (new String []{"http://foo.com" }, config .getAllowedOrigins ().toArray ());
196+ assertTrue (config .getAllowCredentials ());
197+ }
198+
199+ @ Test // SPR-13468
200+ public void methodLevelComposedAnnotation () throws Exception {
201+ this .handlerMapping .registerHandler (new MethodLevelMappingWithComposedAnnotation ());
202+
203+ this .request .setRequestURI ("/foo" );
204+ HandlerExecutionChain chain = this .handlerMapping .getHandler (request );
205+ CorsConfiguration config = getCorsConfiguration (chain , false );
206+ assertNotNull (config );
207+ assertArrayEquals (new String []{"GET" }, config .getAllowedMethods ().toArray ());
208+ assertArrayEquals (new String []{"http://foo.com" }, config .getAllowedOrigins ().toArray ());
209+ assertTrue (config .getAllowCredentials ());
210+ }
211+
183212 @ Test
184213 public void preFlightRequest () throws Exception {
185214 this .handlerMapping .registerHandler (new MethodLevelController ());
@@ -345,6 +374,33 @@ public void baz() {
345374
346375 }
347376
377+ @ Target ({ElementType .METHOD , ElementType .TYPE })
378+ @ Retention (RetentionPolicy .RUNTIME )
379+ @ CrossOrigin
380+ private @interface ComposedCrossOrigin {
381+ String [] origins () default {};
382+ String allowCredentials () default "" ;
383+ }
384+
385+ @ Controller
386+ @ ComposedCrossOrigin (origins = "http://foo.com" , allowCredentials = "true" )
387+ private static class ClassLevelMappingWithComposedAnnotation {
388+
389+ @ RequestMapping (path = "/foo" , method = RequestMethod .GET )
390+ public void foo () {
391+ }
392+ }
393+
394+
395+ @ Controller
396+ private static class MethodLevelMappingWithComposedAnnotation {
397+
398+ @ RequestMapping (path = "/foo" , method = RequestMethod .GET )
399+ @ ComposedCrossOrigin (origins = "http://foo.com" , allowCredentials = "true" )
400+ public void foo () {
401+ }
402+ }
403+
348404 private static class TestRequestMappingInfoHandlerMapping extends RequestMappingHandlerMapping {
349405
350406 public void registerHandler (Object handler ) {
@@ -358,7 +414,7 @@ protected boolean isHandler(Class<?> beanType) {
358414
359415 @ Override
360416 protected RequestMappingInfo getMappingForMethod (Method method , Class <?> handlerType ) {
361- RequestMapping annotation = AnnotationUtils . findAnnotation (method , RequestMapping .class );
417+ RequestMapping annotation = AnnotatedElementUtils . findMergedAnnotation (method , RequestMapping .class );
362418 if (annotation != null ) {
363419 return new RequestMappingInfo (
364420 new PatternsRequestCondition (annotation .value (), getUrlPathHelper (), getPathMatcher (), true , true ),
0 commit comments