如何在 Spring 3 中做一个@CrossOrigin 注释?
How to do a @CrossOrigin annotation in Spring 3?
我想这样做@CrossOrigin:
@CrossOrigin(origins = "http://domain2.com")
@RequestMapping("/{id}")
public Account retrieve(@PathVariable Long id) {
// ...
}
(假设升级到 Spring 4 受到限制)我现在必须用 Spring 3 做的事情如下所示:
public class CORSFilter implements Filter {
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
HttpServletResponse response = (HttpServletResponse) res;
HttpServletRequest request= (HttpServletRequest) req;
response.setHeader("Access-Control-Allow-Origin", "*");
response.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE");
response.setHeader("Access-Control-Allow-Headers", "x-requested-with");
response.setHeader("Access-Control-Expose-Headers", "x-requested-with"); chain.doFilter(req, res);
}
}
请注意 @CrossOrigin
在 Spring 4.2 is here 中的实现来源。
我的问题是:如何在 Spring 3 中做一个 @CrossOrigin 注释?
你没有;该功能直到 4.2 才添加(Spring 4 系列专注于缓存和 CORS 等 Web 技术)。您可以做的最好的事情是您的 Filter
提供的 aspect-oriented 方法,或者如果您想要更细粒度,您可以编写自己的 HandlerInterceptor
来复制添加到 4.2 的功能。
你这样做:
package com.mycompany;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Spring3CorsFilter {}
package com.mycompany;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* The purpose of this class is to emulate the Spring 4 annotation @CORSFilter - using the power of Spring 3
* Note that is is constrained to non-prod environments
*/
public class Spring3CorsFilterHandlerInterceptor extends HandlerInterceptorAdapter {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws
Exception {
if (handler instanceof HandlerMethod) {
HandlerMethod handlerMethod = (HandlerMethod) handler;
// Test if the controller-method is annotated with @Spring3CORSFilter
Spring3CorsFilter filter = handlerMethod.getMethod().getAnnotation(Spring3CorsFilter.class);
if (filter != null ) {
// ... do the filtering
response.setHeader("Access-Control-Allow-Origin", "*");
response.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE");
response.setHeader("Access-Control-Max-Age", "3600");
response.setHeader("Access-Control-Allow-Headers", "x-requested-with");
}
}
return true;
}
}
package com.mycompany;
import com.google.common.base.Optional;
import com.google.common.collect.FluentIterable;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.mock.web.MockHttpServletResponse;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.support.AnnotationConfigContextLoader;
import org.springframework.web.servlet.HandlerExecutionChain;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;
import java.util.Arrays;
import java.util.Set;
import static junit.framework.TestCase.assertTrue;
import static org.junit.Assert.assertFalse;
@RunWith(SpringJUnit4ClassRunner.class)
public class Spring3CorsFilterHandlerInterceptorTest {
@Autowired
private RequestMappingHandlerMapping requestMappingHandlerMapping;
@Test
public void interceptor_is_on_request() throws Exception {
MockHttpServletRequest request = new MockHttpServletRequest("GET",
"/public/version");
HandlerExecutionChain handlerExecutionChain = requestMappingHandlerMapping.getHandler(request);
Optional<Spring3CorsFilterHandlerInterceptor> containsHandler = FluentIterable
.from(Arrays.asList(handlerExecutionChain.getInterceptors()))
.filter(Spring3CorsFilterHandlerInterceptor.class).first();
// Note that this will be present for all requests due to the mapping in spring-security.xml
assertTrue(containsHandler.isPresent());
}
@Test
public void interceptor_is_not_run_on_non_annotated_request() throws Exception {
MockHttpServletRequest request = new MockHttpServletRequest("GET",
"/public/home");
HandlerExecutionChain handlerExecutionChain = requestMappingHandlerMapping.getHandler(request);
Optional<Spring3CorsFilterHandlerInterceptor> containsHandler = FluentIterable
.from(Arrays.asList(handlerExecutionChain.getInterceptors()))
.filter(Spring3CorsFilterHandlerInterceptor.class).first();
MockHttpServletResponse response = new MockHttpServletResponse();
Spring3CorsFilterHandlerInterceptor handlerInterceptor = containsHandler.get();
handlerInterceptor.preHandle(request, response, handlerExecutionChain.getHandler());
Set<String> headerNames = response.getHeaderNames();
assertFalse(headerNames.contains("Access-Control-Allow-Origin"));
assertFalse(headerNames.contains("Access-Control-Allow-Methods"));
assertFalse(headerNames.contains("Access-Control-Max-Age"));
assertFalse(headerNames.contains("Access-Control-Allow-Headers"));
}
@Test
public void interceptor_runs_on_annotated_request() throws Exception {
MockHttpServletRequest request = new MockHttpServletRequest("GET",
"/public/version");
MockHttpServletResponse response = new MockHttpServletResponse();
HandlerExecutionChain handlerExecutionChain = requestMappingHandlerMapping.getHandler(request);
Optional<Spring3CorsFilterHandlerInterceptor> containsHandler = FluentIterable
.from(Arrays.asList(handlerExecutionChain.getInterceptors()))
.filter(Spring3CorsFilterHandlerInterceptor.class).first();
Spring3CorsFilterHandlerInterceptor handlerInterceptor = containsHandler.get();
handlerInterceptor.preHandle(request, response, handlerExecutionChain.getHandler());
Set<String> headerNames = response.getHeaderNames();
assertTrue(headerNames.contains("Access-Control-Allow-Origin"));
assertTrue(headerNames.contains("Access-Control-Allow-Methods"));
assertTrue(headerNames.contains("Access-Control-Max-Age"));
assertTrue(headerNames.contains("Access-Control-Allow-Headers"));
}
}
我想这样做@CrossOrigin:
@CrossOrigin(origins = "http://domain2.com")
@RequestMapping("/{id}")
public Account retrieve(@PathVariable Long id) {
// ...
}
(假设升级到 Spring 4 受到限制)我现在必须用 Spring 3 做的事情如下所示:
public class CORSFilter implements Filter {
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
HttpServletResponse response = (HttpServletResponse) res;
HttpServletRequest request= (HttpServletRequest) req;
response.setHeader("Access-Control-Allow-Origin", "*");
response.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE");
response.setHeader("Access-Control-Allow-Headers", "x-requested-with");
response.setHeader("Access-Control-Expose-Headers", "x-requested-with"); chain.doFilter(req, res);
}
}
请注意 @CrossOrigin
在 Spring 4.2 is here 中的实现来源。
我的问题是:如何在 Spring 3 中做一个 @CrossOrigin 注释?
你没有;该功能直到 4.2 才添加(Spring 4 系列专注于缓存和 CORS 等 Web 技术)。您可以做的最好的事情是您的 Filter
提供的 aspect-oriented 方法,或者如果您想要更细粒度,您可以编写自己的 HandlerInterceptor
来复制添加到 4.2 的功能。
你这样做:
package com.mycompany;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Spring3CorsFilter {}
package com.mycompany;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* The purpose of this class is to emulate the Spring 4 annotation @CORSFilter - using the power of Spring 3
* Note that is is constrained to non-prod environments
*/
public class Spring3CorsFilterHandlerInterceptor extends HandlerInterceptorAdapter {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws
Exception {
if (handler instanceof HandlerMethod) {
HandlerMethod handlerMethod = (HandlerMethod) handler;
// Test if the controller-method is annotated with @Spring3CORSFilter
Spring3CorsFilter filter = handlerMethod.getMethod().getAnnotation(Spring3CorsFilter.class);
if (filter != null ) {
// ... do the filtering
response.setHeader("Access-Control-Allow-Origin", "*");
response.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE");
response.setHeader("Access-Control-Max-Age", "3600");
response.setHeader("Access-Control-Allow-Headers", "x-requested-with");
}
}
return true;
}
}
package com.mycompany;
import com.google.common.base.Optional;
import com.google.common.collect.FluentIterable;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.mock.web.MockHttpServletResponse;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.support.AnnotationConfigContextLoader;
import org.springframework.web.servlet.HandlerExecutionChain;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;
import java.util.Arrays;
import java.util.Set;
import static junit.framework.TestCase.assertTrue;
import static org.junit.Assert.assertFalse;
@RunWith(SpringJUnit4ClassRunner.class)
public class Spring3CorsFilterHandlerInterceptorTest {
@Autowired
private RequestMappingHandlerMapping requestMappingHandlerMapping;
@Test
public void interceptor_is_on_request() throws Exception {
MockHttpServletRequest request = new MockHttpServletRequest("GET",
"/public/version");
HandlerExecutionChain handlerExecutionChain = requestMappingHandlerMapping.getHandler(request);
Optional<Spring3CorsFilterHandlerInterceptor> containsHandler = FluentIterable
.from(Arrays.asList(handlerExecutionChain.getInterceptors()))
.filter(Spring3CorsFilterHandlerInterceptor.class).first();
// Note that this will be present for all requests due to the mapping in spring-security.xml
assertTrue(containsHandler.isPresent());
}
@Test
public void interceptor_is_not_run_on_non_annotated_request() throws Exception {
MockHttpServletRequest request = new MockHttpServletRequest("GET",
"/public/home");
HandlerExecutionChain handlerExecutionChain = requestMappingHandlerMapping.getHandler(request);
Optional<Spring3CorsFilterHandlerInterceptor> containsHandler = FluentIterable
.from(Arrays.asList(handlerExecutionChain.getInterceptors()))
.filter(Spring3CorsFilterHandlerInterceptor.class).first();
MockHttpServletResponse response = new MockHttpServletResponse();
Spring3CorsFilterHandlerInterceptor handlerInterceptor = containsHandler.get();
handlerInterceptor.preHandle(request, response, handlerExecutionChain.getHandler());
Set<String> headerNames = response.getHeaderNames();
assertFalse(headerNames.contains("Access-Control-Allow-Origin"));
assertFalse(headerNames.contains("Access-Control-Allow-Methods"));
assertFalse(headerNames.contains("Access-Control-Max-Age"));
assertFalse(headerNames.contains("Access-Control-Allow-Headers"));
}
@Test
public void interceptor_runs_on_annotated_request() throws Exception {
MockHttpServletRequest request = new MockHttpServletRequest("GET",
"/public/version");
MockHttpServletResponse response = new MockHttpServletResponse();
HandlerExecutionChain handlerExecutionChain = requestMappingHandlerMapping.getHandler(request);
Optional<Spring3CorsFilterHandlerInterceptor> containsHandler = FluentIterable
.from(Arrays.asList(handlerExecutionChain.getInterceptors()))
.filter(Spring3CorsFilterHandlerInterceptor.class).first();
Spring3CorsFilterHandlerInterceptor handlerInterceptor = containsHandler.get();
handlerInterceptor.preHandle(request, response, handlerExecutionChain.getHandler());
Set<String> headerNames = response.getHeaderNames();
assertTrue(headerNames.contains("Access-Control-Allow-Origin"));
assertTrue(headerNames.contains("Access-Control-Allow-Methods"));
assertTrue(headerNames.contains("Access-Control-Max-Age"));
assertTrue(headerNames.contains("Access-Control-Allow-Headers"));
}
}