一.问题产生环境

1.1 为什么会产生跨域问题?

跨域不一定都会有跨域题。

因为跨域问题是浏览器对于ajax请求的一种安全限制;

一个页面发起的 ajax请求,只能是与当前页域名相同的路径,这能有效的阻止跨站攻击;

因此:

跨域问题是针对ajax的一种限制但是这却给我们的开发帯来了不便,而且在实际生产环境中,

肯定会有很多台服务器之间交互,地址和端口都可能不同,怎么办?

1.2  因为公司微服务项目是前后端分离,前后分离后采用了SpringCloud Getway网关技术,这样请求会经历三个流程:

1.前端请求  ===》2.网关 ==》3.后端接口

1.这是单独项目的前段代码:

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>Insert title here</title>
</head>
<!--JQuery在线引用-->
<script src="http://code.jquery.com/jquery-1.10.1.min.js"></script>
<script>
function form_btn(){
var list = JSON.stringify([
{phone: "17320427943", region_code: "620100"},
{phone: "17320427943", region_code: "620100"}
]);
alert(list);
    $.ajax({
        url: "http://localhost:8083/p1upgrade/app/api/delPhoneWarnConf?token=1_grand",
        data: list,
        type: "POST",
contentType: "application/json;charset=utf-8",
        success: function(result){
var list = eval(result);//解析json
       alert(list.data);
    }
    });
} </script>
<body>
<h1>springboot访问第一个html页面</h1>
<button onclick="form_btn()">提交</button>
</body>
</html>

2.网关部分的代码:application.yml

server:
port: 8083
spring:
cloud:
gateway:
routes:
- id: json_route
uri: http://192.168.1.206:8080
predicates:
- Header=Accept, .*json.*
- id: binary_route1
uri: http://192.168.1.206:8080
predicates:
- Path=/p1upgrade/app/api/**
- id: userCheck121
uri: http://192.168.1.206:8080
predicates:
- Path=/p1upgrade/sysManage/userCheck121
- id: appPicManage
uri: http://192.168.1.206:8080
predicates:
- Path=/p1upgrade/picManage/**
- id: path_route
uri: http://192.168.1.206:8080
predicates:
- Path=/**

3.后端接口部分代码:只是问题重现,接口部分代码没有优化

@RequestMapping("delPhoneWarnConf")
public void delPhoneWarnConf(HttpServletResponse rps,
@RequestBody List<Map<String,Object>> list) {
rps.setContentType("application/json;charset=UTF-8");
List<Map<String, Object>> maps=list;
Integer rtcount=0;
for (Object object : maps) {
Map <String,Object> ret = (Map<String, Object>) object;//取出list里面的值转为map
rtcount+=alarmConfigService.delPhoneWarnConf(ret.get("phone").toString(),ret.get("region_code").toString());
}
JSONObject result=SystemUtils.responseBody(0, "success", rtcount);
try {
rps.getWriter().write(result.toString());
} catch (IOException e) {
e.printStackTrace();
}
}

二.错误现象

三.解决办法

目前常用的跨域解决方案有三种:

 (1) jsonp:

                        最早的解决方案,利用script标签可以跨域的原理实现

 缺点:需要服务的支持,

        只能发起get请求

(2)nginx反向代理:

                  思路是利用nginx把跨域反向代理为不跨域,支持各种请求方式

  缺点:需要nginx进行额外配置,语义不清晰

 (3)CORS:

                     思路:规范化的跨域请求方案,安全可靠

      优势:在服务端进行控制是否允许跨域,靠自定义规则。支持各种请求方式。

 缺点:会产生额外的请求

这里一般会采用第三种CORS的跨域方案;

四.什么是CORS

CORS是一个w3c标准,全称是"跨域资源共享"(Cross-origin resource sharing),

但一个请求url的协议,域名,端口三者之间任意与当前页面地址不同即为跨域.它允许阅览器向跨源服务器发送XMLHttpRequest请求,从而克服AJAX只能同源使用的限制.

CORS需要浏览需和服务器同时支持。

     浏览器端:

目前,所有浏览器都支持该功能,浏览器不能低于IE10(E10以下不行)。整个CORS通信过程,都是浏览器自动完成,不需要用户参与。

服务端:

    CORS通信与AAX没有任何差別,因此你不需要改变以前的业务逻辑。只不过,浏览器会在请求中携带一些头信息,我们需要以此判断是否允许其域,然后在响应头中加入一些信息即可。这一般通过过滤器完成即可

五.原理

非简单请求的CORS请求,会在正式通信前进行一次Http查询请求,又称预检请求。

浏览器先请求服务器,当前网页所在域名是否在服务器许可名单中以及可以使用那些HTTP动词和头信息字段,当客户端得到肯定答复时,浏览器才会正式发出XMLHttpRequest请求。否则就会报错。

预检”请求的样板:

OPTIONS /cors HTTP/1.1 
Access-Control-Request-Headers: content-type
Access-Control-Request-Method: POST
Origin: http://localhost:8082
Referer: http://localhost:8082/hello
User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/76.0.3809.132 Safari/537.36
Origin :指出当前请求属于哪个域(协议+域名+端口)。服务会根据这个值决定是否允许其跨域
Access-Control-Request-Method: 接下来会用到的请求方式get,post,put等 
Access-Control-Request-Headers:会额外用到的头信息
User-Agent: 浏览器代理

“预检”请求的响应:

六.实现网关跨域请求

虽然原理比较复杂,但是操作起来没那么难:

  • 浏览器端都由浏览器自动完成,我们无需操心
  • 服务端可以通过拦截器统一实现,不必每次都去进行跨域判定的编写

事实上,Spring已经帮我们写好了CORS的跨域过滤器,内部已经实现了判定逻辑。

  • spring-webmvc:CorsFilter
  • spring-webflux:CorsWebFilter

springcloud_gateway集成的是webflux,所以这里使用的是CorsWebFilter

6.1 在启动类中编写一个配置类,并且注册CorsWebFilter

    @Bean
public CorsWebFilter corsWebFilter() {
//cors跨域对象
CorsConfiguration config = new CorsConfiguration();
config.addAllowedOrigin("http://localhost:8082");// #允许向该服务器提交请求的URI,*表示全部允许,在SpringMVC中,如果设成*,会自动转成当前请求头中的Origin
config.setAllowCredentials(true); // 允许cookies跨域
config.addAllowedMethod("*");// 允许提交请求的方法,*表示全部允许
config.addAllowedHeader("*");// #允许访问的头信息,*表示全部
config.setMaxAge(18000L);// 预检请求的缓存时间(秒),即在这个时间段里,对于相同的跨域请求不会再预检了 //cors过滤对象
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", config);
return new CorsWebFilter(source);
}

6.2 CorsWebFilter实现的是WebFilter,所以也可以在启动类中编写一个配置类,并且注册WebFilter,代码如下,

 1 @Bean
2 public WebFilter corsFilter() {
3 return new WebFilter() {
4
5 @Override
6 public Mono<Void> filter(ServerWebExchange ctx, WebFilterChain chain) {
7 ServerHttpRequest request = ctx.getRequest();
8 if (CorsUtils.isCorsRequest(request)) {
9 ServerHttpResponse response = ctx.getResponse();
10 HttpHeaders headers = response.getHeaders();
11 headers.add(HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN, "*");
12 headers.add(HttpHeaders.ACCESS_CONTROL_ALLOW_HEADERS,"*");
13 headers.add(HttpHeaders.ACCESS_CONTROL_ALLOW_METHODS,"*");
14 headers.add(HttpHeaders.ACCESS_CONTROL_ALLOW_CREDENTIALS, "true");
15 headers.add(HttpHeaders.ACCESS_CONTROL_EXPOSE_HEADERS, "*");
16 headers.add(HttpHeaders.ACCESS_CONTROL_MAX_AGE, "3600");
17 if (request.getMethod() == HttpMethod.OPTIONS) {
18 response.setStatusCode(HttpStatus.OK);
19 return Mono.empty();
20 }
21 }
22 return chain.filter(ctx);
23 }
24
25 };
26 }

以上6.1和6.2两种方案经过测试都是可以的,任意采用一种即可,建议采用6.1代码相对简单点,

6.3 效果展示

 七.因为网关和后端项目都有配置跨域配置,会导致另一种报错

7.1报错现象

响应头信息:

7.2 接口返回状态已经是200,说明已经访问成功了,但是因为项目后端启动之前也配置过跨域配置,所以会与网关配置冲突,导致响应头中返回了两个可接受的域,正常一般是一个,导致报错还存在,
后端启动类跨域配置,主要是对主接口跨越进行控制,
//springcloud-前端跨域问题的解决方案全局配置
@Bean
public WebMvcConfigurerAdapter corsConfigurer() {
return new WebMvcConfigurerAdapter() {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/anzhou/*").allowCredentials(true).allowedMethods("*").maxAge(3600).allowedHeaders("*");
registry.addMapping("/sysManage/*").allowedOrigins("*").allowCredentials(true).allowedMethods("*").maxAge(3600).allowedHeaders("*");
registry.addMapping("/sysManage/*").allowedOrigins("*").allowCredentials(true).allowedMethods("*").maxAge(3600).allowedHeaders("*");
registry.addMapping("/polluSource/*").allowedOrigins("*").allowCredentials(true).allowedMethods("*").maxAge(3600).allowedHeaders("*");
registry.addMapping("/app/api/*").allowedOrigins("*").allowCredentials(true).allowedMethods("*").maxAge(3600).allowedHeaders("*");
registry.addMapping("/checkManagement/*").allowCredentials(true).allowedMethods("*").maxAge(3600).allowedHeaders("*");
registry.addMapping("/minisite/*").allowedOrigins("*").allowCredentials(true).allowedMethods("*").maxAge(3600).allowedHeaders("*");
registry.addMapping("/**").allowedOrigins("*").allowCredentials(true).allowedMethods("*").maxAge(3600).allowedHeaders("*");
}
};
}

所以,应该原因应该是冲突了,但是网关不配置跨域那么请求在网关就被拦截了,根本不会访问到后台接口,所以最后,最好的解决办法就是把跨域配置全都配置在网关中,由网关统一管理,

getway网关跨域问题记录的更多相关文章

  1. jsonp跨域问题记录

    这段时间用H5做移动app开发,遇到不少之前做web的时候不曾遇到的问题,记录一下,共勉-- 首先说一个:js跨域取数的问题 描述:  之前做web都是通过后台获取数据,没考虑过跨域的问题.这次用h5 ...

  2. php跨域问题记录

    记录跨域问题 一.问题 在控制层加了如下代码: header('Access-Control-Allow-Origin: '.$_SERVER['HTTP_ORIGIN'] ); header('Ac ...

  3. Zuul网关跨域问题

    1.跨域就指着协议,域名,端口不一致,出于安全考虑,跨域的资源之间是无法交互的.简单说就是协议不通,域名不通,端口不同都会产生跨域问题 Access-Control-Allow-Origin是HTML ...

  4. nginx跨域问题记录

    现象:访问 toolbox.chinasoft.com 提示如下:Access to Font at 'https://images.chinasoft.com/static-toolbox/styl ...

  5. axios跨域问题记录

    axios({headers: {'X-Requested-With': 'XMLHttpRequest','Content-Type': 'application/json; charset=UTF ...

  6. 限流10万QPS、跨域、过滤器、令牌桶算法-网关Gateway内容都在这儿

    一.微服务网关Spring Cloud Gateway 1.1 导引 文中内容包含:微服务网关限流10万QPS.跨域.过滤器.令牌桶算法. 在构建微服务系统中,必不可少的技术就是网关了,从早期的Zuu ...

  7. 21.SpringCloud实战项目-后台题目类型功能(网关、跨域、路由问题一文搞定)

    SpringCloud实战项目全套学习教程连载中 PassJava 学习教程 简介 PassJava-Learning项目是PassJava(佳必过)项目的学习教程.对架构.业务.技术要点进行讲解. ...

  8. No 'Access-Control-Allow-Origin' header: 跨域问题踩坑记录

    前言 前两周在服务器上部署一个系统时,遇到了跨域问题,这也不是第一次遇到跨域问题了,本来以为解决起来会很顺利,没想到解决过程中遇到了很多坑,所以觉得有必要写一篇博客记录一下这个坑. 问题产生原因 本来 ...

  9. 还在问跨域?本文记录js跨域的多种实现实例

    前言 众所周知,受浏览器同源策略的影响,产生了跨域问题,那么我们应该如何实现跨域呢?本文记录几种跨域的简单实现 前期准备 为了方便测试,我们启动两个服务,10086(就是在这篇博客自动生成的项目,请戳 ...

  10. webuploader 跨域上传demo(还没有写记录一下)

    webuploader 跨域上传demo(还没有写记录一下)

随机推荐

  1. 墨天轮沙龙 | 清华乔嘉林:Apache IoTDB,源于清华,建设开源生态之路

    在6月8日举办的[墨天轮数据库沙龙第七期-开源生态专场]中,清华大学博士,助理研究员,Apache IoTDB PMC 乔嘉林老师分享了<Apache IoTDB,源于清华,建设开源生态之路&g ...

  2. 小程序的image组件

    mode属性:用来制定图片的裁剪和缩放模式:常用属性如下:

  3. ide 安装eval reset插件 Pycharm 永久破解

    ide 安装eval reset插件 Pycharm 永久破解 1.安装eval reset的目的 Jetbrains家的产品有一个很良心的地方,他会允许你试用30天(这个数字写死在代码里了)以评估是 ...

  4. kotlin协程——>通道

    通道:延期的值提供了⼀种便捷的⽅法使单个值在多个协程之间进⾏相互传输.通道提供了⼀种在流中传输 值的⽅法. 通道基础: ⼀个 Channel 是⼀个和 BlockingQueue ⾮常相似的概念.其中 ...

  5. sqlplus登录脚本glogin.sql的配置

    DEFINE sqlprompt=SQL COLUMN sqlprompt NEW_VALUE sqlprompt SET TERMOUT OFF SELECT USER || '@' || NVL( ...

  6. 使用 KubeKey v3.1.1 离线部署原生 Kubernetes v1.28.8 实战

    今天,我将为大家实战演示,如何基于操作系统 openEuler 22.03 LTS SP3,利用 KubeKey 制作 Kubernetes 离线安装包,并实战离线部署 Kubernetes v1.2 ...

  7. 使用IntelliJ IDEA中的Live Templates自定义代码模板

    平时用IntelliJ IDEA写代码的时候,你有没有用过这些快捷方式: 输入main,会弹出自动补全完整的main结构: 输入sout,会弹出自动补全完整的System.out语句: 那么问题来了: ...

  8. redis哨兵模式下主从切换后,php实现自动切换

    redis的哨兵模式,在主服务器挂掉后,会通过选举将对应的从服务器切换为主服务器,以此来达到服务的高可用性. 在业务层面如果主从做了切换可能相对应的服务器IP地址会发生改变,这样会带来程序的的正常运行 ...

  9. 2023NOIP A层联测26 T2 competition

    2023NOIP A层联测26 T2 competition tjm 的做法,很抽象. 考场思路 考虑每道题被做过多少次肯定不现实,那么考虑每一道题有多少次没有做出来. 假设某一次可以做出来题 \(x ...

  10. Auto-Encoding Variational Bayes (VAE原文)、变分推理

    变分自动编码器的大致概念已经理解了快一年多了,但是其中的数学原理还是没有搞懂,在看到相关的变体时,总会被数学公式卡住.下决心搞懂后,在此记录下我的理解. 公式推导--变分下界 这篇文章提出一种拟合数据 ...