@CrossOrigin 通配符 解决跨域问题

痛点:

对很多api接口需要 开放H5 Ajax跨域请求支持 由于环境多套域名不同,而CrossOrigin 原生只支持* 或者具体域名的跨域支持 所以想让CrossOrigin 支持下通配 *.abc.com 支持所有origin 为 abc.com域(包括各种子域名)名来的Ajax 请求支持跨域.

解决思路:

支持通配

@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 等

Spring 默认支持cors 拓展下 Spring 对跨域的处理类

解决方案:

获取 RequestMappingHandlerMapping 设置自定义 MyCorsProcessor 代替DefaultCorsProcessor

/*** 给requestMappingHandlerMapping 对象注入自定义 MyCorsProcessor* @author tomas* @create 2019/8/12**/
@Configuration
@EnableWebMvc
public class MyWebMvcConfig extends DelegatingWebMvcConfiguration {@Beanpublic 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*/@Nullablepublic 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 cors

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 MVC 中的 CORS 的实现。
DispatcherServlet 调用 AbstractHandlerMapping 中的 getHandler() 方法:

  /*** 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 CorsInterceptor


private class CorsInterceptor extends HandlerInterceptorAdapter implements CorsConfigurationSource {@Nullableprivate final CorsConfiguration config;public CorsInterceptor(@Nullable CorsConfiguration config) {this.config = config;}@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)throws Exception {return corsProcessor.processRequest(this.config, request, response);}@Override@Nullablepublic 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)}.*/// 重写此方法 支持通配符 或者支持正则表达式 写法见开头解决方案@Nullableprotected 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);

注意问题:

  1. 如果您正在使用 Spring Security,请确保在 Spring 安全级别启用 CORS,并允许它利用 Spring MVC 级别定义的配置。在 Spring 安全级别启用 CORS:

@EnableWebSecuritypublic class WebSecurityConfig extends WebSecurityConfigurerAdapter {​    @Override​    protected void configure(HttpSecurity http) throws Exception {​        http.cors().and()...​    }}
  1. 全局 CORS 配置

  除了细粒度、基于注释的配置之外,您还可能需要定义一些全局 CORS 配置。这类似于使用筛选器,但可以声明为 Spring MVC 并结合细粒度 @CrossOrigin 配置。默认情况下,所有 origins and GET, HEAD and POST methods 是允许的。

使整个应用程序的 CORS 简化为:


@Configuration@EnableWebMvcpublic class WebConfig extends WebMvcConfigurer {​    @Override​    public void addCorsMappings(CorsRegistry registry) {​        registry.addMapping("/**");​    }}
  1. 基于过滤器的 CORS 支持

  作为上述其他方法的替代,Spring 框架还提供了 CorsFilter。在这种情况下,不用使用@CrossOrigin或``WebMvcConfigurer#addCorsMappings(CorsRegistry),,例如,可以在 Spring Boot 应用程序中声明如下的过滤器:


@Configurationpublic 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、官方文档 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

* * *

转载于:https://www.cnblogs.com/wangdaijun/p/11348463.html

Spring @CrossOrigin 通配符 解决跨域问题相关推荐

  1. Spring Cloud Gateway 解决跨域问题

      注:文中的解决方案在 Spring Cloud 2021.0.4.Spring Boot 2.7.4 版本中得到验证,完美解决,其他版本可参考   请求流程如下图:通过nginx反向代理到网关,在 ...

  2. 注解@CrossOrigin解决跨域问题

    注解@CrossOrigin 出于安全原因,浏览器禁止Ajax调用驻留在当前原点之外的资源.例如,当你在一个标签中检查你的银行账户时,你可以在另一个选项卡上拥有EVILL网站.来自EVILL的脚本不能 ...

  3. Spring Boot:如何解决跨域问题 ?

    目录 跨域问题现象 跨域问题分析 什么是源和跨域 什么是同源策略? Spring Boot:跨域问题解决 1. 创建一个filter解决跨域 2. 基于WebMvcConfigurerAdapter配 ...

  4. SpringMVC注解@CrossOrigin解决跨域问题

    一般的,只要网站的[协议名protocol].[主机host].[端口号port]这三个中的任意一个不同,网站间的数据请求与传输便构成了跨域调用 跨域:浏览同源策略的造成,是浏览器对JavaScrip ...

  5. Spring Boot解决跨域问题

    Spring Boot解决跨域问题 方法一(常用) 实现接口WebMvcConfigurer,并重写addCorsMappings(CorsRegistry registry) @Configurat ...

  6. Spring解决跨域问题方案

    项目中需要前后端分离部署,所以需要解决跨域的问题. 跨域可以在前端通过 JSONP 来解决,但是 JSONP 只可以发送 GET 请求,无法发送其他类型的请求,在 RESTful 风格的应用中,就显得 ...

  7. Spring Boot2 系列教程(十四)CORS 解决跨域问题

    今天和小伙伴们来聊一聊通过CORS解决跨域问题. 同源策略 很多人对跨域有一种误解,以为这是前端的事,和后端没关系,其实不是这样的,说到跨域,就不得不说说浏览器的同源策略. 同源策略是由 Netsca ...

  8. Spring Boot 2.x 跨域问题(多种解决方式)

    参考:https://juejin.im/post/5d438becf265da03de3ae29f 什么是跨域 首先,我们需要了解一下一个URL是怎么组成的: // 协议 + 域名(子域名 + 主域 ...

  9. java 跨域_springboot解决跨域CROS问题,用注解@CrossOrigin

    项目是springboot框架,前后端分离,需要跨域,当前前端可以用JSONP解决,但是java端如何解决呢? 因为是springboot框架,所以好多都可以用注解解决问题,所以就用到了@CrossO ...

最新文章

  1. 光速AStar寻路算法(C++)
  2. php导出csv数据在浏览器中输出提供下载或保存到文件的示例
  3. oracle ora 00283,【案例】Oracle报错ORA-16433非归档丢失redo无法启动的恢复过程
  4. 【mysql】期末数据库复习指南(《数据库系统概率》知识点总结,数据库系统原理,数据库设计课程复习)
  5. 关于WebBrowser(浏览器)控件的调用
  6. C# Chart详细解析
  7. 基于飞桨实现BigGAN生成动漫图像——为艺术创作赋能
  8. css字体加粗(dw怎么在css里字体加粗)
  9. 协同软件市场一盘散沙 春种能否秋收心中没底
  10. vant 表单按钮置灰_Vant Button 按钮
  11. Hbase hive kudu的区别和使用场景
  12. [新人向]MySQL和Navicat下载、安装及使用详细教程
  13. 关于STM32f103c8t6串口下载问题解决
  14. 张艾迪(创始人):世界最高级创始人
  15. CAN Open基础知识
  16. 斐波拉契数列通项公式
  17. $(this).addClass(‘class‘).siblings(‘class‘).removeClass(‘class‘)的作用
  18. Python+pytest+requests 自动化测试框架
  19. 非功能测试-数据库awr报告分析
  20. 华为交换机——批量将端口加入VLAN

热门文章

  1. Arnold+Shave 渲染毛发
  2. Testin云测试:QQ(4.2.0)安卓版客户端可用性优秀
  3. 在VS.NET2003中无法新建C#项
  4. vs studio 2017/2015 连接mysql报错 You have a usable connection already.
  5. 23 Python常用模块(一)
  6. 使用HttpWebRequest请求API接口以及其他网站资源
  7. MongoDB之python简单交互(三)
  8. python的学习笔记(0)之循环的使用1
  9. php获取ios或android通过文件头(header)传过来的坐标,通过百度接口获取具体城市和地址,并存入到session中...
  10. 分布式缓存服务器设计原理