前端面试——跨域问题

跨域问题

浏览器的同源策略

  • 同源是指"协议+域名+端口"三者都相同,它是浏览器最核心也最基本的安全功能,如果缺少了同源策略,浏览器很容易受到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是:
  Accept
  Accept-Language
  Content-Language
  Content-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
    可选。是否允许发送cookie
    
    Access-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的原理是利用

你可能感兴趣的:(前端面试,前端,面试,ajax跨域问题)