From e4c0859d414f6e88f4fb2040a057d8036bfad44b Mon Sep 17 00:00:00 2001 From: Nicolas Labrot Date: Sun, 20 Sep 2015 16:12:13 +0200 Subject: [PATCH] Support @CrossOrigin as a merged composed annotation Issue: SPR-13468 --- .../RequestMappingHandlerMapping.java | 4 +- .../method/annotation/CrossOriginTests.java | 70 +++++++++++++++++-- 2 files changed, 65 insertions(+), 9 deletions(-) diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/RequestMappingHandlerMapping.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/RequestMappingHandlerMapping.java index 2deead181c..e2b52c61db 100644 --- a/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/RequestMappingHandlerMapping.java +++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/RequestMappingHandlerMapping.java @@ -286,8 +286,8 @@ public class RequestMappingHandlerMapping extends RequestMappingInfoHandlerMappi @Override protected CorsConfiguration initCorsConfiguration(Object handler, Method method, RequestMappingInfo mappingInfo) { HandlerMethod handlerMethod = createHandlerMethod(handler, method); - CrossOrigin typeAnnotation = AnnotationUtils.findAnnotation(handlerMethod.getBeanType(), CrossOrigin.class); - CrossOrigin methodAnnotation = AnnotationUtils.findAnnotation(method, CrossOrigin.class); + CrossOrigin typeAnnotation = AnnotatedElementUtils.findMergedAnnotation(handlerMethod.getBeanType(), CrossOrigin.class); + CrossOrigin methodAnnotation = AnnotatedElementUtils.findMergedAnnotation(method, CrossOrigin.class); if (typeAnnotation == null && methodAnnotation == null) { return null; diff --git a/spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/method/annotation/CrossOriginTests.java b/spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/method/annotation/CrossOriginTests.java index 690a743e6e..482adf8789 100644 --- a/spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/method/annotation/CrossOriginTests.java +++ b/spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/method/annotation/CrossOriginTests.java @@ -16,6 +16,10 @@ package org.springframework.web.servlet.mvc.method.annotation; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; import java.lang.reflect.Method; import java.util.Arrays; @@ -25,6 +29,7 @@ import org.junit.Test; import org.junit.rules.ExpectedException; import org.springframework.beans.DirectFieldAccessor; +import org.springframework.core.annotation.AnnotatedElementUtils; import org.springframework.core.annotation.AnnotationUtils; import org.springframework.http.HttpHeaders; import org.springframework.mock.web.test.MockHttpServletRequest; @@ -53,6 +58,7 @@ import static org.junit.Assert.*; * * @author Sebastien Deleuze * @author Sam Brannen + * @author Nicolas Labrot */ public class CrossOriginTests { @@ -79,8 +85,7 @@ public class CrossOriginTests { this.handlerMapping.registerHandler(new MethodLevelController()); MockHttpServletRequest request = new MockHttpServletRequest("GET", "/no"); HandlerExecutionChain chain = this.handlerMapping.getHandler(request); - CorsConfiguration config = getCorsConfiguration(chain, false); - assertNull(config); + assertNull(getCorsConfiguration(chain, false)); } @Test // SPR-12931 @@ -88,8 +93,7 @@ public class CrossOriginTests { this.handlerMapping.registerHandler(new MethodLevelController()); this.request.setRequestURI("/no"); HandlerExecutionChain chain = this.handlerMapping.getHandler(request); - CorsConfiguration config = getCorsConfiguration(chain, false); - assertNull(config); + assertNull(getCorsConfiguration(chain, false)); } @Test // SPR-12931 @@ -98,8 +102,7 @@ public class CrossOriginTests { this.request.setMethod("POST"); this.request.setRequestURI("/no"); HandlerExecutionChain chain = this.handlerMapping.getHandler(request); - CorsConfiguration config = getCorsConfiguration(chain, false); - assertNull(config); + assertNull(getCorsConfiguration(chain, false)); } @Test @@ -180,6 +183,32 @@ public class CrossOriginTests { assertTrue(config.getAllowCredentials()); } + @Test // SPR-13468 + public void classLevelComposedAnnotation() throws Exception { + this.handlerMapping.registerHandler(new ClassLevelMappingWithComposedAnnotation()); + + this.request.setRequestURI("/foo"); + 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()); + assertTrue(config.getAllowCredentials()); + } + + @Test // SPR-13468 + public void methodLevelComposedAnnotation() throws Exception { + this.handlerMapping.registerHandler(new MethodLevelMappingWithComposedAnnotation()); + + this.request.setRequestURI("/foo"); + 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()); + assertTrue(config.getAllowCredentials()); + } + @Test public void preFlightRequest() throws Exception { this.handlerMapping.registerHandler(new MethodLevelController()); @@ -345,6 +374,33 @@ 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 { + + @RequestMapping(path = "/foo", method = RequestMethod.GET) + public void foo() { + } + } + + + @Controller + private static class MethodLevelMappingWithComposedAnnotation { + + @RequestMapping(path = "/foo", method = RequestMethod.GET) + @ComposedCrossOrigin(origins = "http://foo.com", allowCredentials = "true") + public void foo() { + } + } + private static class TestRequestMappingInfoHandlerMapping extends RequestMappingHandlerMapping { public void registerHandler(Object handler) { @@ -358,7 +414,7 @@ public class CrossOriginTests { @Override protected RequestMappingInfo getMappingForMethod(Method method, Class handlerType) { - RequestMapping annotation = AnnotationUtils.findAnnotation(method, RequestMapping.class); + RequestMapping annotation = AnnotatedElementUtils.findMergedAnnotation(method, RequestMapping.class); if (annotation != null) { return new RequestMappingInfo( new PatternsRequestCondition(annotation.value(), getUrlPathHelper(), getPathMatcher(), true, true),