Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to do a @CrossOrigin annotation in Spring 3?

I'd like to do @CrossOrigin like this:

@CrossOrigin(origins = "http://domain2.com")
@RequestMapping("/{id}")
public Account retrieve(@PathVariable Long id) {
    // ...
}

(Assuming upgrading to Spring 4 is constrained) What I have to do at the moment with Spring 3 looks like this:

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

Note that the source for the implementation of @CrossOrigin in Spring 4.2 is here.

My question is: How to do a @CrossOrigin annotation in Spring 3?

like image 416
hawkeye Avatar asked Dec 31 '15 23:12

hawkeye


People also ask

What is @crossorigin annotation in Spring Boot?

CORS using @CrossOrigin Annotation Spring 4.2 has introduced @CrossOrigin annotation to handle Cross-Origin-Resource-Sharing (CORS). This annotation is used at class level as well as method level in RESTful Web service controller. @CrossOrigin annotation is used at method level with @RequestMapping annotation.

How does the @crossorigin annotation derive the Cors configuration?

If the @CrossOrigin annotation is specified at both the class level and the method level, Spring will derive the CORS configuration by combining attributes from both annotations. 2. Spring Boot CORS - Global CORS Configuration

How to use @crossorigin annotation in RESTful web service?

This annotation is used at class level as well as method level in RESTful Web service controller. @CrossOrigin annotation is used at method level with @RequestMapping annotation. If we are using @CrossOrigin at class and method level both then there will be combined effect. @CrossOrigin has following optional elements.

Does spring support Cross-Origin Resource Sharing (CORS)?

This guide walks you through the process of creating a “Hello, World” RESTful web service with Spring that includes headers for Cross-Origin Resource Sharing (CORS) in the response. You can find more information about Spring CORS support in this blog post.


2 Answers

You do it like this:

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"));
    }
}
like image 195
hawkeye Avatar answered Oct 21 '22 21:10

hawkeye


You don't; the functionality wasn't added until 4.2 (the Spring 4 series has had a focus on Web technologies such as caching and CORS). The best you can do is the aspect-oriented approach, which your Filter provides, or if you want more granularity, you could write your own HandlerInterceptor that duplicates the functionality added to 4.2.

like image 41
chrylis -cautiouslyoptimistic- Avatar answered Oct 21 '22 23:10

chrylis -cautiouslyoptimistic-