什么是跨域?
跨域, 是指浏览器不能执行其他网站的脚本。浏览器出于安全方面的考虑,只允许与同源下的接口交互,也就是浏览器的同源策略。同源策略会阻止跨域。
什么是同源?
同源必须要做到以下三点:同协议,同域名,同端口。
被请求页面的协议、域名、端口中任意一个与当前页面不同即为跨域。
| 当前页面URL | 被请求页面URL | 是否同源 | 原因 |
|---|---|---|---|
| www.123.com | www.123.com/do | 是 | 协议、域名、端口都相同 |
| www.123.com | www.123.com | 否 | 协议不同(https/http) |
| www.123.com | 123.com | 否 | 域名不同(www.123.com/123.com) |
| www.123.com:8080 | www.123.com:9090 | 否 | 端口不同(8080/9090) |
跨域的解决办法
- JSONP
原理:
1.同源策略只限制 Ajax 请求,不限制 script 标签加载 js。可以通过 script 标签请求 资源,并提前写好接收函数。
2.服务器收到请求后,从 callback 参数得到 fn,把原始数据(假设是 {a:1})处理 后变成 handleData({a:1})
3.script 里的资源加载后会当成 js 执行,相当于执行 handleData({a:1}), 即可在预 定义的 handleData 函数里处理数据
代码:服务器端:
const http = require('http')
const url = require('url')
http.createServer((req, res)=>{
let urlObj = url.parse(req.url, true)
if(urlObj.pathname === '/getWeather'){
let data = {city:'杭州',weather:'晴天'}
res.end(`${urlObj.query.callback}(${JSON.stringify(data)})`)
}else{
req.writeHead(404,'404 not found')
res.end('404 Not Found')
}
}).listen(2020)
console.log('监听 http://127.0.0.1:2020')
前端页面js:
<script>
function showData(data) {
console.log(data) //
}
</script>
<script src="http://127.0.0.1:2020/getWeather?callback=showData"></script>
在函数showData中即可获得数据。
- CORS
原理:发送Ajax请求时,浏览器发现该请求不符合同源策略,会给该请求加一个请求头:Origin。后台收到请求后,如果确定接受请求则在返回结果中加入一个响应头:Access-Control-Allow-Origin;浏览器判断该响应头中是否包含 当前Origin 的值,如果有则浏览器会处理响应,我们就可以拿到响应数据,如果没有浏览器直接驳回。
服务器代码:
const http = require('http')
const url = require('url')
http.createServer((req, res)=>{
let urlObj = url.parse(req.url, true)
if(urlObj.pathname === '/getWeather'){
res.setHeader('Access-Control-Allow-Origin', 'https://www.baidu.com')
res.end(JSON.stringify( { city: 'hangzhou', weather: 'sunny' } ))
}else {
res.writeHead(404, 'Not Found')
res.end('not found')
}
}).listen(9090)
console.log('可以在 https://www.baidu.com 中访问getWeather接口了!')
在 www.baidu.com 控制台获取数据:
fetch('http://127.0.0.1:9090/getWeather')
.then(res => res.json())
.then(data => console.log(data))
即可获得数据:{city: "hangzhou", weather: "sunny"}
- 服务器中转()
服务器代码:
const http = require('http')
const url = require('url')
http.createServer((req, res)=>{
let urlObj = url.parse(req.url, true)
if(urlObj.pathname === '/bridge'){
http.get(urlObj.query.url,req =>{
let text = ''
req.on('data', data => text += data)
req.on('end',()=>{
res.setHeader('Access-Control-Allow-Origin','*')
res.end(text)
})
})
}
}).listen(8888)
console.log('监听 http://127.0.0.1:8888')
//当前代码在其他网页下运行
fetch('http://localhost:8888/bridge?url='+encodeURIComponent('http://baidu.com'))
.then(res=>res.text())
.then(data => console.log(data))