如何在 Spring 3 中进行 @CrossOrigin 注释?

Posted

技术标签:

【中文标题】如何在 Spring 3 中进行 @CrossOrigin 注释?【英文标题】:How to do a @CrossOrigin annotation in Spring 3? 【发布时间】:2016-04-05 18:49:48 【问题描述】:

我想像这样@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 注解?

【问题讨论】:

【参考方案1】:

你这样做:

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"));
    

【讨论】:

【参考方案2】:

你没有;直到 4.2 才添加该功能(Spring 4 系列专注于 Web 技术,例如缓存和 CORS)。您可以做的最好的事情是您的Filter 提供的面向方面的方法,或者如果您想要更细化,您可以编写自己的HandlerInterceptor 来复制添加到4.2 的功能。

【讨论】:

你能给我指出一个类似的 HandlerInterceptor 例子吗? (我假设它将用作注释) @hawkeye 您必须自己检查和解释注释。我的建议是查看 Spring 4.2 源代码。

以上是关于如何在 Spring 3 中进行 @CrossOrigin 注释?的主要内容,如果未能解决你的问题,请参考以下文章

如何在 Spring 3 MVC 应用程序中对文件上传进行病毒扫描[关闭]

如何在 Spring Security 3.1 中进行没有身份验证和授权的并发会话控制

如何在内存中进行单元测试 Spring-Jersey

如何在 Grails 中使用 spring security rest 插件进行身份验证

使用 spring 3.0 进行用户身份验证

如何使用Spring WebClient同时进行多个调用?