跨域问题

浏览器的同源策略

  • 同源是指"协议+域名+端口"三者都相同,它是浏览器最核心也最基本的安全功能,如果缺少了同源策略,浏览器很容易受到XSS、CSFR等攻击。

  • http://www.a.com:3000/index.html这个网址,协议是http,域名是www.a.com,端口是3000(我们经常看的网址没有,是因为默认端口80可以省略)

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

    • cookie(同ip不同端口可共享)、localStorage、sessionStorage和indexDB无法读取
    • DOM和JS对象无法获得
    • AJAX请求不能发送

跨域解决方案

跨域:从一个网页去请求另一个网页的资源,只要协议、域名、端口其中一个不同,就被当作是跨域

跨域资源共享(CORS)

浏览器将CORS跨域请求分为简单请求非简单请求,对这两种请求的处理是不一样的

只要满足两个条件就属于简单请求,否则属于非简单请求:

使用以下方法之一:head、get、post
请求的Header是:AcceptAccept-LanguageContent-LanguageContent-Type(只限于三个值:application/x-www-form-urlencoded、multipart/form-data、text/plain)
简单请求

对于简单请求,浏览器直接发出CORS请求。

在头信息中,增加一个Origin字段,用来说明本次请求来自哪个源(协议+域名+端口),服务器根据这个值,决定是否同意这次请求。

  • 如果Origin指定的源,不在许可范围内,服务器返回的responseHeader中就不会包含Access-Control-Allow-Origin字段,浏览器抛出错误,被XMLHttpRequest的onerror回调函数捕获

  • 如果Origin指定的源在许可范围内,服务器返回响应(会多出几个字段)

    Access-Control-Allow-Origin: http://api.bob.com
    必须。值为请求时的Origin或*,*表示接受任意域名的请求Access-Control-Allow-Credentials: true
    可选。是否允许发送cookieAccess-Control-Expose-Headers: FooBar //getREsponseHeader('FooBar')可以返回FooBar的值
    可选。CORS请求时,XMLHttpRequest对象的getResponseHeader()方法只能拿到6个基本字段:
    Cache-Control、Expires、Last-Modified、Content-Type、Content-Language、Pragma
    如果想拿到其他字段就必须在Access-Control-Expose-Headers里面指定Content-Type: text/html; charset=utf-8
    

withCredentials属性:

CORS请求默认不发送cookie和http认证信息,若要带上cookie,需要服务端和客户端同时设置,否则即使服务器同意带上cookie,浏览器也不会发送,或者浏览器发送,服务器也不会处理

但是如果省略withCredentials设置,有的浏览器还是会发送cookie,这种情况下需要显式关闭withCredentials

服务端设置:Access-Control-Allow-Credentials: true客户端打开withCredentials属性:
var xhr = new XMLHttpRequest();
xhr.withCredentials = true;

注意:如果要发送cookie,Access-Control-Allow-Origin就不能设为*,必须指明与请求网页一致的域名,并且cookie也遵循同源策略,只有服务器域名设置的cookie才会上传,并且跨源原网页代码中的document.cookie也无法读取服务器域名下的cookie

非简单请求

对服务器有特殊要求的请求,比如请求方法是PUT、DELETE,或Content-Type字段的类型是application/json

预检请求:非简单请求的CORS请求,会在正式通信之前,增加一次HTTP查询请求,称为预检请求

浏览器先询问服务器,当前网页所在的域名是否在服务器许可名单之中,以及可以使用哪些HTTP动词和Headers,只有得到肯定答复,浏览器才会发出正式的XMLHttpRequest请求,否则报错

例如:

var url = 'http://api.alice.com/cors';
var xhr = new XMLHttpRequest();
xhr.open('PUT', url, true);
xhr.setRequestHeader('X-Custom-Header', 'value');
xhr.send();

浏览器发现这是一个非简单请求,于是先发送一个预检请求

预检请求的HTTP头信息:

OPTIONS /cors HTTP/1.1  - 请求方法是OPTIONS,表示这个请求是用来询问的
Origin: http://api.bob.com  - 关键字段,表示请求来自哪个源
Access-Control-Request-Method: PUT - 必须。列出浏览器CORS请求会用到哪些方法
Access-Control-Request-Headers: X-Custom-Header - 指定浏览器CORS请求会额外发送的Headers
Host: api.alice.com
Accept-Language: en-US
Connection: keep-alive
User-Agent: Mozilla/5.0...

服务器收到预检请求之后,检查了OriginAccess-Control-Request-MethodAccess-Control-Request-Headers之后,确认允许跨源请求就可以做出回应,responseHeader中关键是Access-Control-Allow-Origin,若服务器否定了预检请求,返回一个正常http回应,没有任何CORS相关的头信息字段,浏览器随之报错,同普通请求

其他CORS相关字段:

Access-Control-Allow-Methods: GET, POST, PUT
必须。值为逗号分隔的字符串,表示服务器支持的所有(为避免多次预检请求)跨域请求的方法Access-Control-Allow-Headers: X-Custom-Header
若requestHeader有Access-Control-Request-Headers,则responseHeader就有Access-Control-Allow-Headers
值为逗号分隔的字符串,表示服务器支持的所有Header字段Access-Control-Allow-Credentials: true
同简单请求Access-Control-Max-Age: 1728000 //s
可选。本次预检请求的有效期,在缓存期间内不能发出另一条预检请求

一旦服务器通过了"预检"请求,以后每次浏览器正常的CORS请求,就都跟简单请求一样,会有一个Origin头信息字段。服务器的回应,也都会有一个Access-Control-Allow-Origin头信息字段

通过JSONP跨域
  • jsonp的原理是利用<script>标签没有跨域限制,通过<script>标签的src属性,发送带有callback参数的get请求,服务端将接口返回数据拼凑到callback函数中,返回给浏览器,浏览器解析执行,从而前端拿到callback函数返回的数据

  • 注意:服务端返回的是函数调用,如:

    handleCallback({"name": "amethyst", "color": "blueviolet"})
    
  • server.js

  • const express=require("express");const app=express();app.all('/login',(request,response)=>{var data={name:'amethyst',color:'blueviolet'}var str=JSON.stringify(data);var cb=request.query.callback;response.send(`${cb}(${str})`);
    });app.listen(8080,()=>{console.log('8080端口监听中...');
    });
    
  • 前台使用:

  • const script=document.createElement('script');
    script.type='text/javascript';//传参并指定回调函数名为onBack
    script.src='http://localhost:8080/login?callback=onBack';
    document.head.appendChild(script);//执行回调函数
    function onBack(res){console.log(res);
    }
    
  • 执行结果:

过程:

  • 网页端插入一个script标签,src指向目标url(url后面加上query,?callback=函数名

  • 后端处理函数接收到请求,得到函数名,比如handle(handle函数定义在前端)

  • 后端用handle包装数据,返回给浏览器(返回的content-type必须是text/javascript;charset=utf-8

  • 网页端script内容加载完成得到handle(data)

  • 浏览器发现内容是js(查看content-type),则调用js解释器执行handle(data)

优点:

  • 它不像XMLHttpRequest对象实现的Ajax请求那样受到同源策略的限制
  • 它的兼容性更好,在更加古老的浏览器中都可以运行
  • 不需要XMLHttpRequest或ActiveX的支持,并且在请求完毕后可以通过调用callback的方式回传结果

缺点:

  • 它只支持GET请求而不支持POST等其它类型的HTTP请求
  • 它只支持跨域HTTP请求这种情况,不能解决不同域的两个页面之间如何进行数据通信的问题(JavaScript调用)
  • 安全性低,容易遭受XSS攻击,因为我们拿到的是对方接口的数据作为js执行,如果得到的是一个很危险的js,获取了用户信息和cookies,这时执行了js就会出现安全问题,因此使用jsonp跨域就必须保证jsonp服务是安全可信的
  • 在失败的时候不会返回各种HTTP状态码
Node中间件跨域(http-proxy-middleware),两次跨域

实现原理:同源策略是浏览器需要遵循的标准,而服务器向服务器发送请求则无需遵循同源策略,使用一个代理服务器,设置Access-Control-Allow-Origin等字段便可以解决浏览器和代理服务器之间的跨域问题,而服务器之间没有限制,便实现了浏览器和服务器之间跨域通信,并且可以实现代理多个请求到不同的服务器

代理服务器所需要做的:

  • 接受客户端的请求
  • 将请求转发给服务器
  • 拿到服务器响应数据
  • 将响应转发给客户端

在开发环境下配置代理:

方法一

直接在package.json文件中配置proxy

方法二

新建一个setupProxy.js文件,在里面配置proxy,这种方法可以同时设置转发多个请求

const proxy=require('http-proxy-middleware');module.exports=function(app){app.use(proxy('/api1',{target:'http://localhost:5000',changeOrigin:true,pathRewrite:{'^/api1':''}}),proxy('/api2',{target:'http://localhost:5001',changeOrigin:true,pathRewrite:{'^/api2':''}}));
}
nginx反向代理

实现原理和node中间件代理类似,只需要修改nginx的配置即可解决跨域问题,支持所有浏览器,支持session,可修改cookie信息

// proxy服务器
server {listen       81;server_name  www.domain1.com;location / {proxy_pass   http://www.domain2.com:8080;  #反向代理proxy_cookie_domain www.domain2.com www.domain1.com; #修改cookie里域名index  index.html index.htm;# 当用webpack-dev-server等中间件代理接口访问nignx时,此时无浏览器参与,故没有同源限制,#下面的跨域配置可不启用add_header Access-Control-Allow-Origin http://www.domain1.com;  #当前端只跨域不带cookie时,可为*add_header Access-Control-Allow-Credentials true;}
}
websocket协议跨域

websocket protocol是html5的一种新的协议。,实现了浏览器与服务器全双工通信,同时允许跨域通讯,是server push技术的一种很好的实现。原生websocket api使用起来不太方便,我们使用socket.io,它很好地封装了websocket接口,提供了更简单、灵活的接口,也对不支持websocket的浏览器提供了向下兼容

前端面试——跨域问题相关推荐

  1. 前端解决跨域问题的8种方案(最新最全)

    .同源策略如下: URL 说明 是否允许通信 http://www.a.com/a.js http://www.a.com/b.js 同一域名下 允许 http://www.a.com/lab/a.j ...

  2. 「跨域」利用node.js实践前端各种跨域方式(上)

    前言 常言道,"读万卷书,不如行万里路".技术的学习也是如此,唯有实践才能更清楚的明白原理和加深印象,因此本文会利用node.js对前端的各种跨域方式进行实践,强烈建议一步一步跟着 ...

  3. 前端常见跨域问题解决方案

    前端常见跨域解决方案(全) 什么是跨域? 跨域是指一个域下的文档或脚本试图去请求另一个域下的资源,这里跨域是广义的. 广义的跨域: 1.) 资源跳转: A链接.重定向.表单提交 2.) 资源嵌入: & ...

  4. 前端处理跨域的几种方式

    什么是跨域? 跨域是指一个域下的文档或脚本试图去请求另一个域下的资源,这里跨域是广义的. 广义的跨域: 1.资源跳转:A链接.重定向.表单提交 2.资源嵌入: <link>.<scr ...

  5. 前端解决跨域问题的8种方案

    2019独角兽企业重金招聘Python工程师标准>>> 1.同源策略如下: URL 说明 是否允许通信 http://www.a.com/a.js http://www.a.com/ ...

  6. [转] js前端解决跨域问题的8种方案(最新最全)

    [转] js前端解决跨域问题的8种方案(最新最全) 参考文章: (1)[转] js前端解决跨域问题的8种方案(最新最全) (2)https://www.cnblogs.com/chris-oil/p/ ...

  7. 前端解决跨域的九种方法

    什么是跨域? 跨域是指一个域下的文档或脚本试图去请求另一个域下的资源,这里跨域是广义的. 广义的跨域: 1.资源跳转:A链接.重定向.表单提交 2.资源嵌入: <link>.<scr ...

  8. 前端常见跨域解决方案

    前端常见跨域解决方案 参考文章: (1)前端常见跨域解决方案 (2)https://www.cnblogs.com/plBlog/p/11430457.html 备忘一下.

  9. [跨域]前端解决跨域问题

    1.同源策略如下: URL 说明 是否允许通信 http://www.a.com/a.js http://www.a.com/b.js 同一域名下 允许 http://www.a.com/lab/a. ...

最新文章

  1. 【pytorch】nn.conv1d的使用
  2. java 1.8 tar.gz_linux安装java1.8
  3. linux下安装MySQL出错file /usr/share/mysql/charsets/latin2.xml from install of MySQL-......
  4. java lodop打印_Java的云打印Lodop
  5. 域名怎么绑定ip_服务器怎么绑定域名?
  6. bzoj4380[POI2015]Myjnie dp
  7. pytorch 指定卡1_在pytorch中指定显卡
  8. 成都将于1月27日开启数字人民币红包活动
  9. 与context的关系_Go中的Context超时和关闭是如何实现的呢?
  10. 3.1EDA和数据描述: 探索性数据分析
  11. [BZOJ3684]大朋友和多叉树
  12. HDU 3247 Resource Archiver(AC自动机 + 状压DP + bfs预处理)题解
  13. 安卓加密视频播放器使用教程
  14. python爬取qq群成员_Python爬取QQ群群员
  15. 浙大PAT甲级题目1081-1100详细代码解答|标准答案|C++语言|浙软机考
  16. html页面睡眠函数,JavaScript sleep睡眠函数
  17. 桌面版 Linux 为什么打不过 Window?Linus 现身说法
  18. android型号手机怎么截图,安卓手机怎么截图 手机截图超简单图文教程
  19. 火狐浏览器点击下载按钮没反应
  20. vscode如何运行python文件_vscode怎么运行.py文件_编程开发工具

热门文章

  1. 边界值测试实战:关于佣金计算问题
  2. C# @Page指令中的AutoEventWireup,CodeBehind,Inherits
  3. 论文阅读:Epsolute: Efficiently Querying Databases While Providing Differential Privacy
  4. 技术篇丨音频监控应用现状分析
  5. 对计算机选购的注意事项,2021大学生选购电脑,8条注意事项!
  6. 以防作弊,ChatGPT 遭教育部「拉黑」:师生禁用!
  7. 单服务器部署CTFd+whale踩坑
  8. C/C++ 使用 API 函数 ShellExecuteEx 实现文件打印
  9. vue实战-加入购物车一系列操作
  10. 1000个常用建模材质,提高效率