跨域问题的情况、原理及解决方案
一、跨域与同源策略
1.1 同源策略
ajax请求时,浏览器要求当前网页和server必须同源(安全),服务端爬虫等时则不一定需要同源,因为这个同源要求是浏览器提出的保护机制
同源:协议、域名、端口,三者必须一致
加载图片、css 、js 可无视同源策略
<img />
可用于统计打点,可使用第三方统计服务<link /> 、<script>
可使用CDN ,CDN一般都是外域<script>
可实现JSONP
1
2
3<img src=跨域的图片地址/>
<link href=跨域的css地址/>
<script src=跨域的js地址></script>
1.2 出现跨域问题的几种情况
- 即不符合同源策略
1 | 1 域名不同 |
二、跨域报错的原理
- 现有域名A:
http://localhost:8888
和域名B:http://localhost:8887
- 当在域名A中访问域名B时会报如下错误:
- 报错的原因指出了:是在域名B中没有设置
Access-Control-Allow-Origin
,若没有在域名B中做这个设置,则不能跨域访问它,所以跨域的本质是被访问的一方需要做设置,允许访问方访问 - 事实上,域名A访问域名B时是可以正常访问的,只是浏览器在访问的返回结果中解析到
Access-Control-Allow-Origin
没有设置允许域名A访问,所以报错,所以报错的本质不是不能访问而是浏览器做了拦截工作,报错提醒
三、跨域问题的解决方案
3.1 标签方式
在访问时,通过
script
、image
、link
等标签的src
和ref
直接访问1
<script src="http://loaclhost:8887"></script>
通过标签直接访问时,不会有跨域的问题,无论被访问方是否设置了允许访问
3.2 JSONP方式
JSONP返回的不是数据,而是一段js代码,执行前端已经定义好的函数,同时将后端的参数携带在函数中执行
借助下面的例子理解:
1 <script src="http://localhost:8002/jsonp.js?username=xxx&callback=abc"></script>等价于
1
2
3
4
5 <script>
abc(
{ name: 'xxx' }
)
</script>
JSONP方式本质还是利用了标签方式,
<script>
可绕过跨域限制服务器可以任意动态拼接数据返回
<script>
就可以获得跨域的数据,只要服务端愿意返回
1 | //jsonp.html |
1 | //jsonp.js |
- JSONP方式发送请求时也可以传入参数,
- 服务端接收参数后,会动态拼接成需要的格式,然后返回客户端
3.3 CORS方式
设置
Access-Control-Allow-Origin
'Access-Control-Allow-Origin': '*'
: 允许所有第三方访问
1
2
3
4
5
6
7
8
9
10
11
12
13const http = require('http')
http.createServer(function (request, response) {
console.log('request come', request.url)
//设置允许跨域访问
response.writeHead(200, {
'Access-Control-Allow-Origin': '*'
})
response.end('123')
}).listen(8887)
console.log('server listening on 8887')Access-Control-Allow-Origin': 'http://127.0.0.1:8888
:设置指定网址可以访问
1
2
3
4//设置允许跨域访问
response.writeHead(200, {
'Access-Control-Allow-Origin': 'http://127.0.0.1:8888'
})
四、跨域限制及预请求验证
4.1 跨域请求
以下是默认允许跨域跨域的,不需要额外设置
允许跨域的HTTP方法:
- GET
- HEAD
- POST
允许跨域的Content-Type:
- text/plain
- multipart/form-data
- application/x-www-form-urlencoded
请求头限制,只允许一下请求头:
允许跨域的请求头:
Accept
Accept-Language
Content-Language
Content-Type
4.2 预请求验证
预请求是指在存在跨域问题时,当发送正式请求前,浏览器会先发送一个OPTIONS
请求,验证服务端是否有声明默认设置之外的请求头、请求方法等可以访问,若声明了则正式请求跨域访问,否则报错。预请求本质就是通过OPTIONS
请求验证了服务端是否允许访问
跨域预请求设置:
- 在被访问端设置(服务端)
Access-Control-Allow-Origin
: 允许访问的域名Access-Control-Allow-Methods
:设置默认方法之外可以访问的方法Access-Control-Allow-Headers
:设置默认请求头之外的跨域访问的请求头Access-Control-Max-Age
:设置预请求最大可以持续时间(在该时间段内可以直接发正式请求,不用每次都发预请求)
1 | //设置允许跨域访问 |