@CrossOrigin 通配符 解決跨域問題html
支持通配java
@CrossOrigin(origins = {"*.abc.com"}) 通配 主域+任意子域 www.abc.com order.api.abc.com dev.order.abc.com 等
@CrossOrigin(origins = {"*.order.abc.com"}) 通配order子域 子域名 dev.order.abc.com test.order.abc.com uat.order.abc.com 等web
Spring 默認支持cors 拓展下 Spring 對跨域的處理類ajax
獲取 RequestMappingHandlerMapping 設置自定義 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; } } AntPathMatcher pathMatcher = new AntPathMatcher("|"); for (String allowedOrigin :config.getAllowedOrigins()) { if (requestOrigin.equalsIgnoreCase(allowedOrigin)) { return requestOrigin; } //推薦方式:正則 注意(CrossOrigin(origins = {"*.abc.com"}) ) 主域會匹配主域+子域 origins = {"*.pay.abc.com"} 子域名只會匹配子域 if(pathMatcher.isPattern(allowedOrigin)&&pathMatcher.match(allowedOrigin,requestOrigin)){ return requestOrigin; } //不推薦方式:寫死 if(allowedOrigin.contains("*.abc.com")&& requestOrigin.contains("abc.com")){ return requestOrigin; } } return null; } }
Spring MVC 的文檔這樣說:
Spring MVC 的 HandlerMapping 實現內置支持 CORS, 在成功映射一個請求到一個 handler 以後, HandlerMapping 會檢查 CORS 配置以採起下一步動做。
https://docs.spring.io/spring/docs/current/spring-framework-reference/web.html#mvc-cors-processing
Spring MVC 會在找到 handler 後經過添加一個攔截器來檢查 CORS 配置。spring
下面來看一下 Spring MVC 中的 CORS 的實現。
DispatcherServlet 調用 AbstractHandlerMapping 中的 getHandler() 方法:api
/** * 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; }
對於 Ajax 請求 getCorsHandlerExecutionChain 自動加上一個 CorsInterceptor 的攔截器:跨域
/** * 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中 私有class CorsInterceptorspring-mvc
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; } }
CorsInterceptor中preHandle方法 實際處理 processRequest的是AbstractHandlerMapping.this.corsProcessor安全
這個corsProcessor =new DefaultCorsProcessor() 是一個默認的跨域處理類
咱們的重點就是 重寫DefaultCorsProcessor的checkOrigin 方法
@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 中在真正 invoke handler 以前會先調用攔截器: 從而經過加的 cors 攔截器阻止請求。
doDispatch 方法:
// 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);
@EnableWebSecurity public class WebSecurityConfig extends WebSecurityConfigurerAdapter { @Override protected void configure(HttpSecurity http) throws Exception { http.cors().and()... } }
除了細粒度、基於註釋的配置以外,您還可能須要定義一些全局 CORS 配置。這相似於使用篩選器,但能夠聲明爲 Spring MVC 並結合細粒度 @CrossOrigin 配置。默認狀況下,全部 origins and GET, HEAD and POST methods 是容許的。
使整個應用程序的 CORS 簡化爲:
@Configuration @EnableWebMvc public class WebConfig extends WebMvcConfigurer { @Override public void addCorsMappings(CorsRegistry registry) { registry.addMapping("/**"); } }
做爲上述其餘方法的替代,Spring 框架還提供了 CorsFilter。在這種狀況下,不用使用@CrossOrigin或``WebMvcConfigurer#addCorsMappings(CorsRegistry)
,,例如,能夠在 Spring Boot 應用程序中聲明以下的過濾器:
@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; } }
感謝 @大神張林峯老師 @王昆老師 @中睿老師 給出的寶貴意見
一、官方文檔 https://spring.io/blog/2015/06/08/cors-support-in-spring-framework
二、https://blog.csdn.net/weixin_33713503/article/details/88039675
https://www.jianshu.com/p/d05303d34222
http://www.javashuo.com/article/p-ruiaqgqh-hk.html
二、https://blog.csdn.net/taiyangnimeide/article/details/78305131
三、https://blog.csdn.net/snowin1994/article/details/53035433
* * *