当前位置:   article > 正文

什么是跨域?跨域问题怎么解决?_接口跨域是什么意思

接口跨域是什么意思

目录

一、什么是跨域?

二、为什么会出现跨域问题?

三、常见的跨域场景

四、跨域解决方法

1、JSONP

(1)JSONP原理

(2)JSONP和AJAX对比

(3)JSONP优缺点

(4)JSONP的流程(以第三方API地址为例,不必考虑后台程序)

(5)jQuery的jsonp形式

2、CORS

(1)CORS原理

(2)CORS优缺点

(3)用法

3、WebSocket

4、httpClient内部转发

5、使用nginx搭建企业级接口网关方式

6、设置document.domain解决无法读取非同源网页的 Cookie问题

7、跨文档通信 API:window.postMessage()

本地项目中遇到的跨域


一、什么是跨域?

跨域是指一个域下的文档或脚本试图去请求另一个域下的资源,这里跨域是广义的。

广义的跨域:
1.) 资源跳转: A链接、重定向、表单提交
2.) 资源嵌入: link script img frame等dom标签,还有样式中background:url()、@font-face()等文件外链
3.) 脚本请求: js发起的ajax请求、dom和js对象的跨域操作等

当一个请求url的协议、域名、端口三者之间任意一个与当前页面url不同即为跨域

跨域并不是请求发不出去,请求能发出去,服务端能收到请求并正常返回结果,只是结果被浏览器拦截了

特别说明两点:

第一:如果是协议和端口造成的跨域问题“前台”是无能为力的。

第二:在跨域问题上,域仅仅是通过“URL的首部”来识别而不会根据域名对应的IP地址是否相同来判断。“URL的首部”可以理解为“协议, 域名和端口必须匹配”

二、为什么会出现跨域问题?

前端部分其实我们通常所说的跨域是狭义的,是由浏览器同源策略限制的一类请求场景。

出于浏览器的同源策略限制。同源策略/SOP(Sameoriginpolicy)是一种约定,它是浏览器最核心也最基本的安全功能,如果缺少了同源策略,浏览器很容易受到XSS、CSFR等攻击,则浏览器的正常功能可能都会受到影响。可以说Web是构建在同源策略基础之上的,浏览器只是针对同源策略的一种实现。同源策略会阻止一个域的javascript脚本和另外一个域的内容进行交互。所谓同源(即指在同一个域)就是两个页面具有相同的协议(protocol),主机(host)和端口号(port)

同源策略限制以下几种行为:

1.) Cookie、LocalStorage 和 IndexDB 无法读取

2.) DOM 和 Js对象无法获得

3.) AJAX 请求不能发送

但是有三个标签是允许跨域加载资源:
1.<img src=XXX> 
2.<link href=XXX> 
3.<script src=XXX>

三、常见的跨域场景

当前页面URL被请求页面URL是否跨域原因
http://www.test.com/http://www.test.com/index.html同源(协议、域名、端口号相同)
http://www.test.com/https://www.test.com/index.html跨域协议不同(http/https)
http://www.test.com/http://www.baidu.com/跨域主域名不同(test/baidu)
http://www.test.com/http://blog.test.com/跨域子域名不同(www/blog)
http://www.test.com:8080/http://www.test.com:7001/跨域端口号不同(8080/7001)
http://www.domain.com/a.jshttp://192.168.4.12/b.js跨域域名和域名对应相同IP,也非同源

四、跨域解决方法

但所有的跨域都必须经过信息提供方的允许。如果未经允许即可获取,那是浏览器同源策略出现漏洞。

1、JSONP

(1)JSONP原理

利用 <script> 元素的这个开放策略,网页可以得到从其他来源动态产生的 JSON 数据。服务器收到请求后,将数据放在一个指定名字的回调函数的参数位置传回来。JSONP请求一定需要对方的服务器做支持才可以。

(2)JSONP和AJAX对比

JSONP和AJAX相同,都是客户端向服务器端发送请求,从服务器端获取数据的方式。但AJAX属于同源策略,JSONP属于非同源策略(跨域请求), Jquery中ajax的核心是通过 XmlHttpRequest获取非本页内容,而jsonp的核心则是动态添加<script>标签来调用服务器提供的 js脚本。当我们正常地请求一个JSON数据的时候,服务端返回的是一串JSON类型的数据,而我们使用 JSONP模式来请求数据的时候服务端返回的是一段可执行的JavaScript代码。

(3)JSONP优缺点

JSONP优点是兼容性好,可用于解决主流浏览器的跨域数据访问的问题。缺点是仅支持get方法具有局限性。

(4)JSONP的流程(以第三方API地址为例,不必考虑后台程序)

  • 声明一个回调函数,其函数名(如fn)当做参数值,要传递给跨域请求数据的服务器,函数形参为要获取目标数据(服务器返回的data)。

  • 创建一个<script>标签,把那个跨域的API数据接口地址,赋值给script的src,还要在这个地址中向服务器传递该函数名(可以通过问号传参:?callback=fn)。

  • 服务器接收到请求后,需要进行特殊的处理:把传递进来的函数名和它需要给你的数据拼接成一个字符串,例如:传递进去的函数名是fn,它准备好的数据是fn([{"name":"jianshu"}])。

  • 最后服务器把准备的数据通过HTTP协议返回给客户端,客户端再调用执行之前声明的回调函数(fn),对返回的数据进行操作。

  1. <script type="text/javascript" src="http://test.com/jsonServerResponse?callback=fn"></script>
  2. //向服务器test.com发出请求,该请求的查询字符串有一个callback参数,用来指定回调函数的名字
  3. //处理服务器返回回调函数的数据
  4. <script type="text/javascript">
  5. function fn(data) {
  6. alert(data.msg);
  7. }
  8. </script>
其中 fn 是客户端注册的回调的函数,目的获取跨域服务器上的json数据后,对数据进行在处理
最后服务器返回给客户端数据的格式为:
fn({ msg:'this  is  json  data'})

(5)jQuery的jsonp形式

JSONP都是GET和异步请求的,不存在其他的请求方式和同步请求,且jQuery默认就会给JSONP的请求清除缓存。
  1. $.ajax({
  2. url:"http://crossdomain.com/jsonServerResponse",
  3. dataType:"jsonp", //数据类型为jsonp
  4. type:"get",//可以省略
  5. jsonpCallback:"fn",//->自定义传递给服务器的函数名,而不是使用jQuery自动生成的,可省略
  6. jsonp:"jsonp",//->把传递函数名的那个形参callback变为jsonp,可省略
  7. success:function (data){
  8. console.log(data);
  9. }
  10. error : function() {
  11. alert('fail');
  12. }
  13. });

用法:

①dataType改为jsonp

②jsonp : "jsonp"—发送到后端实际为http://crossdomain.com/jsonServerResponse?jsonp=jQueryxxx ,可以不修改

③后端获取get请求中的jsonp

④构造回调结构

  1. //后端
  2. String jsonpCallback = request.getParameter("jsonp");
  3. //构造回调函数格式jsonp(数据)
  4. resp.getWriter().println(jsonp+"("+jsonObject.toJSONString()+")");

2、CORS

(1)CORS原理

CORS 是跨域资源分享(Cross-Origin Resource Sharing)的缩写。它是 W3C 标准,属于跨源 AJAX 请求的根本解决方法。

整个CORS通信过程,都是浏览器自动完成,不需要用户参与。对于开发者来说,CORS通信与同源的AJAX通信没有差别,代码完全一样。浏览器一旦发现AJAX请求跨源,就会自动添加一些附加的头信息,有时还会多出一次附加的请求,但用户不会有感觉。因此,实现CORS通信的关键是服务器。只要服务器实现了CORS接口,就可以跨源通信


(2)CORS优缺点

CORS要求浏览器(>IE10)和服务器的同时支持,是跨域的根本解决方法,由浏览器自动完成

优点在于功能更加强大支持各种HTTP Method,缺点是兼容性不如JSONP。

(3)用法

a、前端代码(需要判断浏览器是否支持情况)

  1. function createCORSRequest(method, url) {
  2. var xhr = new XMLHttpRequest();
  3. if ("withCredentials" in xhr) {
  4. // 此时即支持CORS的情况
  5. // 检查XMLHttpRequest对象是否有“withCredentials”属性
  6. // “withCredentials”仅存在于XMLHTTPRequest2对象里
  7. xhr.open(method, url, true);
  8. } else if (typeof!= "undefined") {
  9. // 否则检查是否支持XDomainRequest,IE8和IE9支持
  10. // XDomainRequest仅存在于IE中,是IE用于支持CORS请求的方式
  11. xhr = new XDomainRequest();
  12. xhr.open(method, url);
  13. } else {
  14. // 否则,浏览器不支持CORS
  15. xhr = null;
  16. }
  17. return xhr;
  18. }
  19. var xhr = createCORSRequest('GET', url);
  20. if (!xhr) {
  21. throw new Error('CORS not supported');
  22. }

b、服务器端

  1. response.addHeader(‘Access-Control-Allow-Origin:*’);//允许所有来源访问
  2. response.addHeader(‘Access-Control-Allow-Method:POST,GET’);//允许访问的方式

3、WebSocket

Websocket是HTML5的一个持久化的协议,它实现了浏览器与服务器的全双工通信,同时也是跨域的一种解决方案。WebSocket和HTTP都是应用层协议,都基于 TCP 协议。但是 WebSocket 是一种双向通信协议,在建立连接之后,WebSocket 的 server 与 client 都能主动向对方发送或接收数据。同时,WebSocket 在建立连接时需要借助 HTTP 协议,连接建立好了之后 client 与 server 之间的双向通信就与 HTTP 无关了。

原生WebSocket API使用起来不太方便,我们使用Socket.io,它很好地封装了webSocket接口,提供了更简单、灵活的接口,也对不支持webSocket的浏览器提供了向下兼容。
  1. //前端代码:
  2. <div>user input:<input type="text"></div>
  3. <script src="./socket.io.js"></script>
  4. <script>
  5. var socket = io('http://www.domain2.com:8080');
  6. // 连接成功处理
  7. socket.on('connect', function() {
  8. // 监听服务端消息
  9. socket.on('message', function(msg) {
  10. console.log('data from server: ---> ' + msg);
  11. });
  12. // 监听服务端关闭
  13. socket.on('disconnect', function() {
  14. console.log('Server socket has closed.');
  15. });
  16. });
  17. document.getElementsByTagName('input')[0].onblur = function() {
  18. socket.send(this.value);
  19. };
  20. </script>
  1. //Nodejs socket后台:
  2. var http = require('http');
  3. var socket = require('socket.io');
  4. // 启http服务
  5. var server = http.createServer(function(req, res) {
  6. res.writeHead(200, {
  7. 'Content-type': 'text/html'
  8. });
  9. res.end();
  10. });
  11. server.listen('8080');
  12. console.log('Server is running at port 8080...');
  13. // 监听socket连接
  14. socket.listen(server).on('connection', function(client) {
  15. // 接收信息
  16. client.on('message', function(msg) {
  17. client.send('hello:' + msg);
  18. console.log('data from client: ---> ' + msg);
  19. });
  20. // 断开处理
  21. client.on('disconnect', function() {
  22. console.log('Client socket has closed.');
  23. });
  24. });

4、httpClient内部转发

实现原理很简单,若想在B站点中通过Ajax访问A站点获取结果,固然有ajax跨域问题,但在B站点中访问B站点获取结果,不存在跨域问题,这种方式实际上是在B站点中ajax请求访问B站点的HttpClient,再通过HttpClient转发请求获取A站点的数据结果。但这种方式产生了两次请求,效率低,但内部请求,抓包工具无法分析,安全。
 

  1. $.ajax({
  2. type : "GET",
  3. async : false,
  4. url : "http://b.b.com:8080/B/FromAjaxservlet?userName=644064",
  5. dataType : "json",
  6. success : function(data) {
  7. alert(data["userName"]);
  8. },
  9. error : function() {
  10. alert('fail');
  11. }
  12. });
  1. @WebServlet("/FromAjaxservlet")
  2. public class FromAjaxservlet extends HttpServlet{
  3. protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
  4. try {
  5. //创建默认连接
  6. CloseableHttpClient httpClient = HttpClients.createDefault();
  7. //创建HttpGet对象,处理get请求,转发到A站点
  8. HttpGet httpGet = new HttpGet("http://a.a.com:8080/A/FromServlet?userName="+req.getParameter("userName"));
  9. //执行
  10. CloseableHttpResponse response = httpClient.execute(httpGet);
  11. int code = response.getStatusLine().getStatusCode();
  12. //获取状态
  13. System.out.println("http请求结果为:"+code);
  14. if(code == 200){
  15. //获取A站点返回的结果
  16. String result = EntityUtils.toString(response.getEntity());
  17. System.out.println(result);
  18. //把结果返回给B站点
  19. resp.getWriter().print(result);
  20. }
  21. response.close();
  22. httpClient.close();
  23. } catch (Exception e) {
  24. }
  25. }
  26. }

5、使用nginx搭建企业级接口网关方式

www.a.a.com不能直接请求www.b.b.com的内容,可以通过nginx,根据同域名,但项目名不同进行区分。什么意思呢?这么说可能有点抽象。假设我们公司域名叫www.nginxtest.com

当我们需要访问www.a.a.com通过www.nginxtest.com/A访问,并通过nginx转发到www.a.a.com

当我们需要访问www.b.b.com通过www.nginxtest.com/B访问,并通过nginx转发到www.a.a.com

我们访问公司的域名时,是"同源"的,只是项目名不同,此时项目名的作用只是为了区分,方便转发。如果你还不理解的话,先看看是怎么进行配置的:
 

  1. server {
  2. listen 80;
  3. server_name www.nginxtest.com;
  4. location /A {
  5. proxy_pass http://a.a.com:81;
  6. index index.html index.htm;
  7. }
  8. location /B {
  9. proxy_pass http://b.b.com:81;
  10. index index.html index.htm;
  11. }
  12. }

我们访问以www.nginxtest.com开头且端口为80的网址,nginx将会进行拦截匹配,若项目名为A,则分发到a.a.com:81。实际上就是通过"同源"的域名,不同的项目名进行区分,通过nginx拦截匹配,转发到对应的网址。整个过程,两次请求,第一次请求nginx服务器,第二次nginx服务器通过拦截匹配分发到对应的网址。

6、设置document.domain解决无法读取非同源网页的 Cookie问题

因为浏览器是通过document.domain属性来检查两个页面是否同源,因此只要通过设置相同的document.domain,两个页面就可以共享Cookie

  1. // 两个页面都设置
  2. document.domain = 'test.com';

7、跨文档通信 API:window.postMessage()

如果两个网页不同源,就无法拿到对方的DOM。典型的例子是iframe窗口和window.open方法打开的窗口,它们与父窗口无法通信。HTML5为了解决这个问题,引入了一个全新的API:跨文档通信 API(Cross-document messaging)。这个API为window对象新增了一个window.postMessage方法,允许跨窗口通信,不论这两个窗口是否同源。postMessage方法的第一个参数是具体的信息内容,第二个参数是接收消息的窗口的源(origin),即"协议 + 域名 + 端口"。也可以设为*,表示不限制域名,向所有窗口发送。

调用postMessage方法实现父窗口http://test1.com向子窗口http://test2.com发消息(子窗口同样可以通过该方法发送消息给父窗口)

  1. // 父窗口打开一个子窗口
  2. var openWindow = window.open('http://test2.com', 'title');
  3. // 父窗口向子窗口发消息(第一个参数代表发送的内容,第二个参数代表接收消息窗口的url)
  4. openWindow.postMessage('Nice to meet you!', 'http://test2.com');

调用message事件,监听对方发送的消息

  1. // 监听 message 消息
  2. window.addEventListener('message', function (e) {
  3. console.log(e.source); // e.source 发送消息的窗口
  4. console.log(e.origin); // e.origin 消息发向的网址
  5. console.log(e.data); // e.data 发送的消息
  6. },false);

本地项目中遇到的跨域

 想在本地调另一台服务器上的接口,但是一直报跨域的错误,错误如下:

 ajax:其中的dftApiContextFile是配置文件中的该接口的路径

 最后是以下方式解决的,在这里记录一下:

1、将要调的接口的包和自己的项目在本地启动

2、配置Nginx

(1)将Nginx解压

(2)修改nginx.conf(这里给一个示例,主要是修改为自己的端口号)

      nginx通过proxy_pass_http 配置代理站点,upstream实现负载均衡。

  1. #user nobody;
  2. worker_processes 1;
  3. #error_log logs/error.log;
  4. #error_log logs/error.log notice;
  5. #error_log logs/error.log info;
  6. #pid logs/nginx.pid;
  7. events {
  8. worker_connections 1024;
  9. }
  10. http {
  11. include mime.types;
  12. default_type application/octet-stream;
  13. #log_format main '$remote_addr - $remote_user [$time_local] "$request" '
  14. # '$status $body_bytes_sent "$http_referer" '
  15. # '"$http_user_agent" "$http_x_forwarded_for"';
  16. #access_log logs/access.log main;
  17. sendfile on;
  18. #tcp_nopush on;
  19. #keepalive_timeout 0;
  20. keepalive_timeout 65;
  21. #gzip on;
  22. upstream gxweb_pool {
  23. ip_hash;
  24. server 127.0.0.1:8089;
  25. }
  26. upstream dataweb_pool {
  27. ip_hash;
  28. #server 127.0.0.1:8081;
  29. server 127.0.0.1:9091;
  30. }
  31. upstream resource_pool {
  32. ip_hash;
  33. #server 127.0.0.1:8081;
  34. server 127.0.0.1:8085;
  35. }
  36. server {
  37. listen 80;
  38. server_name localhost;
  39. #charset koi8-r;
  40. #access_log logs/host.access.log main;
  41. location / {
  42. root html;
  43. index index.html index.htm;
  44. }
  45. location ^~ /apply {
  46. proxy_set_header Host $host;
  47. proxy_set_header X-Real-IP $remote_addr;
  48. proxy_set_header REMOTE-HOST $remote_addr;
  49. proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
  50. proxy_pass http://gxweb_pool/apply;
  51. proxy_redirect off;
  52. }
  53. location ^~ /portal {
  54. proxy_set_header Host $host;
  55. proxy_set_header X-Real-IP $remote_addr;
  56. proxy_set_header REMOTE-HOST $remote_addr;
  57. proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
  58. proxy_pass http://dataweb_pool/portal;
  59. proxy_redirect off;
  60. }
  61. location ^~ /dataweb{
  62. proxy_set_header Host $host;
  63. proxy_set_header X-Real-IP $remote_addr;
  64. proxy_set_header REMOTE-HOST $remote_addr;
  65. proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
  66. proxy_pass http://dataweb_pool/portal;
  67. proxy_redirect off;
  68. }
  69. location ^~ /resource{
  70. proxy_set_header Host $host;
  71. proxy_set_header X-Real-IP $remote_addr;
  72. proxy_set_header REMOTE-HOST $remote_addr;
  73. proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
  74. proxy_pass http://resource_pool/resource;
  75. proxy_redirect off;
  76. }
  77. #error_page 404 /404.html;
  78. # redirect server error pages to the static page /50x.html
  79. #
  80. error_page 500 502 503 504 /50x.html;
  81. location = /50x.html {
  82. root html;
  83. }
  84. # proxy the PHP scripts to Apache listening on 127.0.0.1:80
  85. #
  86. #location ~ \.php$ {
  87. # proxy_pass http://127.0.0.1;
  88. #}
  89. # pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000
  90. #
  91. #location ~ \.php$ {
  92. # root html;
  93. # fastcgi_pass 127.0.0.1:9000;
  94. # fastcgi_index index.php;
  95. # fastcgi_param SCRIPT_FILENAME /scripts$fastcgi_script_name;
  96. # include fastcgi_params;
  97. #}
  98. # deny access to .htaccess files, if Apache's document root
  99. # concurs with nginx's one
  100. #
  101. #location ~ /\.ht {
  102. # deny all;
  103. #}
  104. }
  105. # another virtual host using mix of IP-, name-, and port-based configuration
  106. #
  107. #server {
  108. # listen 8000;
  109. # listen somename:8080;
  110. # server_name somename alias another.alias;
  111. # location / {
  112. # root html;
  113. # index index.html index.htm;
  114. # }
  115. #}
  116. # HTTPS server
  117. #
  118. #server {
  119. # listen 443 ssl;
  120. # server_name localhost;
  121. # ssl_certificate cert.pem;
  122. # ssl_certificate_key cert.key;
  123. # ssl_session_cache shared:SSL:1m;
  124. # ssl_session_timeout 5m;
  125. # ssl_ciphers HIGH:!aNULL:!MD5;
  126. # ssl_prefer_server_ciphers on;
  127. # location / {
  128. # root html;
  129. # index index.html index.htm;
  130. # }
  131. #}
  132. }

(3)启动Nginx    ----运行nginx.exe 

3、测试

(1)访问接口包   ---如http://localhost/resource

(2)访问自己的项目 ---如http://localhost/portal

          看下代理有没有成功

(3)测试下接口能不能调通,返回数据

(4)看项目中调接口的地方有没有数据显示

参考http://www.imooc.com/article/40074

       https://blog.csdn.net/itcats_cn/article/details/82318092

       https://blog.csdn.net/weixin_42039396/article/details/80040099

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/神奇cpp/article/detail/773681
推荐阅读
相关标签
  

闽ICP备14008679号