Spring @CrossOrigin wildcard solve cross-domain problems

@CrossOrigin wildcard solve cross-domain problems

Pain points:

For many api interface needs to open H5 Ajax support cross-domain requests due to the different environment sets the domain name, and CrossOrigin native only supports cross-domain support *, or specific domain names so let down CrossOrigin support wildcard * .abc.com support all origin as abc .com domain (including various sub-domain) names to support cross-domain Ajax request.

Solutions:

Support wildcard

@CrossOrigin(origins = {"*.abc.com"})

Spring default support cors expand under the category of cross-domain processing Spring

solution:

Get RequestMappingHandlerMapping provided instead of the custom MyCorsProcessor DefaultCorsProcessor

/**
 * 给requestMappingHandlerMapping 对象注入自定义 MyCorsProcessor
 * @author tomas
 * @create 2019/8/12
 **/
@Configuration
@EnableWebMvc
public class MyWebMvcConfig extends DelegatingWebMvcConfiguration {
    @Bean
    public RequestMappingHandlerMapping requestMappingHandlerMapping() {
        RequestMappingHandlerMapping handlerMapping = super.requestMappingHandlerMapping();
        handlerMapping.setCorsProcessor(new MyCorsProcessor());
        return handlerMapping;
    }
}
/**
 * MyCorsProcessor 描述
 * 自定义 如果xxx.com域下的请求允许跨域
 *
 * @author tomas
 * @create 2019/8/12
 **/
public class MyCorsProcessor extends DefaultCorsProcessor {

    /**
     * Check the origin of the request against the configured allowed origins.
     * @param requestOrigin the origin to check
     * @return the origin to use for the response, or {@code null} which
     * means the request origin is not allowed
     */
    @Nullable
    public String checkOrigin(CorsConfiguration config, @Nullable String requestOrigin) {
        if (!StringUtils.hasText(requestOrigin)) {
            return null;
        }
        if (ObjectUtils.isEmpty(config.getAllowedOrigins())) {
            return null;
        }
        if (config.getAllowedOrigins().contains(CorsConfiguration.ALL)) {
            if (config.getAllowCredentials() != Boolean.TRUE) {
                return CorsConfiguration.ALL;
            }
            else {
                return requestOrigin;
            }
        }
        for (String allowedOrigin :config.getAllowedOrigins()) {
            if (requestOrigin.equalsIgnoreCase(allowedOrigin)) {
                return requestOrigin;
            }
             //https://blog.abc.com/api/getXxx?id=1
            //推荐方式:正则 注意(CrossOrigin(origins = {"*.abc.com"}) ) 此方法会匹配上面的 url 
            if(requestOrigin.matches(new StringBuffer(".").append(allowedOrigin).append(".*").toString())){
                return requestOrigin;
            }
            //不推荐方式:写死
            if(allowedOrigin.contains("*.abc.com")&& requestOrigin.contains("abc.com")){
                return requestOrigin;
            }
        }
        return null;
    }
}

Principle Analysis:

Spring mvc cors

Spring MVC documents say:
Spring MVC HandlerMapping realization of built-in support CORS, after successfully mapped a request to a handler, HandlerMapping checks the CORS configuration to take the next action.
https://docs.spring.io/spring/docs/current/spring-framework-reference/web.html#mvc-cors-processing
the Spring MVC will be checked after finding the handler CORS configuration by adding a blocker.

The following look at Spring MVC realization of the CORS.
DispatcherServlet call getHandler AbstractHandlerMapping in () method:

  /**
     * Look up a handler for the given request, falling back to the default
     * handler if no specific one is found.
     * @param request current HTTP request
     * @return the corresponding handler instance, or the default handler
     * @see #getHandlerInternal
     */
@Override
@Nullable
public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
        Object handler = getHandlerInternal(request);
        if (handler == null) {
            handler = getDefaultHandler();
        }
        if (handler == null) {
            return null;
        }
        // Bean name or resolved handler?
        if (handler instanceof String) {
            String handlerName = (String) handler;
            handler = obtainApplicationContext().getBean(handlerName);
        }

        HandlerExecutionChain executionChain = getHandlerExecutionChain(handler, request);
        if (CorsUtils.isCorsRequest(request)) {
            CorsConfiguration globalConfig = this.globalCorsConfigSource.getCorsConfiguration(request);
            CorsConfiguration handlerConfig = getCorsConfiguration(handler, request);
            CorsConfiguration config = (globalConfig != null ? globalConfig.combine(handlerConfig) : handlerConfig);
            executionChain = getCorsHandlerExecutionChain(request, executionChain, config);
        }
        return executionChain;
    }

For Ajax request getCorsHandlerExecutionChain automatically add a CorsInterceptor interceptor:

 /**
     * Update the HandlerExecutionChain for CORS-related handling.
     * <p>For pre-flight requests, the default implementation replaces the selected
     * handler with a simple HttpRequestHandler that invokes the configured
     * {@link #setCorsProcessor}.
     * <p>For actual requests, the default implementation inserts a
     * HandlerInterceptor that makes CORS-related checks and adds CORS headers.
     * @param request the current request
     * @param chain the handler chain
     * @param config the applicable CORS configuration (possibly {@code null})
     * @since 4.2
     */
protected HandlerExecutionChain getCorsHandlerExecutionChain(HttpServletRequest request,
            HandlerExecutionChain chain, @Nullable CorsConfiguration config) {

        if (CorsUtils.isPreFlightRequest(request)) {
            HandlerInterceptor[] interceptors = chain.getInterceptors();
            chain = new HandlerExecutionChain(new PreFlightHandler(config), interceptors);
        }
        else {
            chain.addInterceptor(new CorsInterceptor(config));
        }
        return chain;
    }

AbstractHandlerMapping in private class CorsInterceptor

 
private class CorsInterceptor extends HandlerInterceptorAdapter implements CorsConfigurationSource {

        @Nullable
        private final CorsConfiguration config;
        public CorsInterceptor(@Nullable CorsConfiguration config) {
            this.config = config;
        }
        @Override
        public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
                throws Exception {
            return corsProcessor.processRequest(this.config, request, response);
        }
        @Override
        @Nullable
        public CorsConfiguration getCorsConfiguration(HttpServletRequest request) {
            return this.config;
        }
    }

In the actual processing methods CorsInterceptor preHandle processRequest is AbstractHandlerMapping.this.corsProcessor

This corsProcessor = new DefaultCorsProcessor () is a cross-domain processing default class

Our focus is to rewrite checkOrigin method of DefaultCorsProcessor

 
    @Override
    @SuppressWarnings("resource")
    public boolean processRequest(@Nullable CorsConfiguration config, HttpServletRequest request,
            HttpServletResponse response) throws IOException {

        if (!CorsUtils.isCorsRequest(request)) {
            return true;
        }
                ......
        return handleInternal(serverRequest, serverResponse, config, preFlightRequest);
    }


    /**
     * Handle the given request.
     */
    protected boolean handleInternal(ServerHttpRequest request, ServerHttpResponse response,
            CorsConfiguration config, boolean preFlightRequest) throws IOException {

        String requestOrigin = request.getHeaders().getOrigin();
        String allowOrigin = checkOrigin(config, requestOrigin);
        HttpHeaders responseHeaders = response.getHeaders();

        responseHeaders.addAll(HttpHeaders.VARY, Arrays.asList(HttpHeaders.ORIGIN,
                HttpHeaders.ACCESS_CONTROL_REQUEST_METHOD, HttpHeaders.ACCESS_CONTROL_REQUEST_HEADERS));

        if (allowOrigin == null) {
            logger.debug("Rejecting CORS request because '" + requestOrigin + "' origin is not allowed");
            rejectRequest(response);
            return false;
        }

        ..........
        response.flush();
        return true;
    }

    /**
     * Check the origin and determine the origin for the response. The default
     * implementation simply delegates to
     * {@link org.springframework.web.cors.CorsConfiguration#checkOrigin(String)}.
     */

       // 重写此方法 支持通配符 或者支持正则表达式 写法见开头解决方案
    @Nullable
    protected String checkOrigin(CorsConfiguration config, @Nullable String requestOrigin) {
        return config.checkOrigin(requestOrigin);
    }
}

dispatcherServlet will first call interceptor before actually invoke handler: preventing cors request by adding the interceptor.
doDispatch method:

 // Determine handler adapter for the current request.
                HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());

                // Process last-modified header, if supported by the handler.
                String method = request.getMethod();
                boolean isGet = "GET".equals(method);
                if (isGet || "HEAD".equals(method)) {
                    long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
                    if (logger.isDebugEnabled()) {
                        logger.debug("Last-Modified value for [" + getRequestUri(request) + "] is: " + lastModified);
                    }
                    if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
                        return;
                    }
                }

                if (!mappedHandler.applyPreHandle(processedRequest, response)) {
                    return;
                }

                // Actually invoke the handler.
                mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

                if (asyncManager.isConcurrentHandlingStarted()) {
                    return;
                }

                applyDefaultViewName(processedRequest, mv);
                mappedHandler.applyPostHandle(processedRequest, response, mv);

Attention to the problem:

  1. If you are using Spring Security, be sure to enable CORS security level in the Spring, and allow it to use Spring MVC level defined configuration. Enabling CORS in Spring Security Level :

@EnableWebSecurity

public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

​    @Override

​    protected void configure(HttpSecurity http) throws Exception {

​        http.cors().and()...

​    }

}
  1. Global CORS configuration

  In addition to fine-grained, annotation-based configuration, you also may need to define some global CORS configuration. This is similar to using a filter, but it can be declared as Spring MVC combined with fine-grained @CrossOrigin configuration. By default, all origins and GET, HEAD and POST methods are allowed.

The entire application CORS simplified to:


@Configuration

@EnableWebMvc

public class WebConfig extends WebMvcConfigurer {

​    @Override

​    public void addCorsMappings(CorsRegistry registry) {

​        registry.addMapping("/**");

​    }

}
  1. Based on CORS support filters

  As an alternative to the other methods described above, Spring framework provides CorsFilter . In this case, without using @CrossOrigin或``WebMvcConfigurer#addCorsMappings(CorsRegistry),, e.g., filters can be declared in the following Spring Boot application:


@Configuration

public class MyConfiguration {

​    @Bean

​    public FilterRegistrationBean corsFilter() {

​        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();

​        CorsConfiguration config = new CorsConfiguration();

​        config.setAllowCredentials(true);

​        config.addAllowedOrigin("http://domain1.com");

​        config.addAllowedHeader("*");

​        config.addAllowedMethod("*");

​        source.registerCorsConfiguration("/**", config);

​        FilterRegistrationBean bean = new FilterRegistrationBean(new CorsFilter(source));

​        bean.setOrder(0);

​        return bean;

​    }

}

1, the official document https://spring.io/blog/2015/06/08/cors-support-in-spring-framework

2、https://blog.csdn.net/weixin_33713503/article/details/88039675

https://www.jianshu.com/p/d05303d34222

https://www.cnblogs.com/helloz/p/10961039.html

2、https://blog.csdn.net/taiyangnimeide/article/details/78305131

3、https://blog.csdn.net/snowin1994/article/details/53035433

* * *

Guess you like

Origin www.cnblogs.com/wangdaijun/p/11348463.html