一、什么是同源策略?
同源策略限制从一个源加载的文档或者脚本如何与来自另一个源的资源进行交互。这是一个用于隔离潜在恶意文件的关键的安全机制。非同一个源的定义:
- 不同协议,https 和 http,如 https://www.baidu.com 和 http://www.baidu.com
- 不同端口,如 http://127.0.0.1:8080 和 http://127.0.0.1:8090
- 不同域名,如 a.com 和 b.com
二、什么是跨域?跨域有几种实现形式?
跨域就是不同源的资源之间的交互。正是因为同源策略,才会出现跨域这种问题。跨域的实现方式有:
- JSONP
- CORS
- 降域
- postMessage
三、JSONP 的原理是什么
利用 html 的 script 标签可以引入其他 JS 资源而且不引起跨域问题,原理是 JS 是被下载到当前浏览器环境执行,所以就不算跨域,就像平常通过 cdn 引入 jQuery 一样。因此,我们可以通过这种方式,让后端返回数据,具体流程如下:
- 定义数据处理函数 _fun
- 创建 script 标签,src 的地址执行后端接口,最后加个参数 callback = _fun
- 服务端在收到请求后,解析参数,计算返还数据,输出 fun(data) 字符串。
- fun(data) 会放到 script 标签做为 js 执行。此时会调用 fun 函数,将 data 做为参数。
四、CORS 是什么?
CORS(Cross-Origin Resource Sharing)跨域资源共享,是一种允许 Web 应用服务器进行跨域访问控制机制,从而使跨域数据传输得以安全进行。具体通过在响应头的 Header 里面加上 Access-Control-Allow-Origin
属性,允许相应的源地址访问来实现。
五、演示三种以上跨域的解决方式
1. JSONP
通过 node + express 来搭建本地服务器,实现 JSONP 效果。 JS 代码:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>JSONP 演示</title>
</head>
<body>
<button id="getData">点击获取数据</button>
<div>
<h1>数据展示</h1>
<p id="dataShow"></p>
</div>
<script>
const getData = document.getElementById('getData')
getData.addEventListener('click', (e) => {
const jsonpTag = document.getElementById('jsonp')
if(jsonpTag){
jsonpTag.remove()
}
let scriptTag = document.createElement('script')
scriptTag.src = 'http://127.0.0.1:3000/jsonp?callback=jsonp'
scriptTag.id = 'jsonp'
document.querySelector('body').appendChild(scriptTag)
})
function jsonp(data) {
const dataShow = document.getElementById('dataShow')
const str = JSON.stringify(data)
dataShow.innerHTML = str
}
</script>
</body>
服务端代码
const express = require('express')
const Mock = require('mockjs')
const router = express.Router()
router.use('/', (req, res, next) => {
console.log('jsonp')
next()
})
router.get('/', (req, res, next) => {
const data = Mock.mock({
'list|1-10': [{
'id|+1': 1,
'name|1-3': '@FIRST'
}]
})
const callback = req.query.callback
const resData = `${callback}(${JSON.stringify(data)})`
res.end(resData)
})
module.exports = router
实际演示
可以看到这请求和相应不是同源的,因为端口不同。
JSONP 演示
2.CORS
客户端代码
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>CORS 演示</title>
</head>
<body>
<button id="getData">点击获取数据</button>
<div>
<h1>数据展示</h1>
<p id="dataShow"></p>
</div>
<script>
const getData = document.getElementById('getData')
const dataShow = document.getElementById('dataShow')
getData.addEventListener('click', (e) => {
const xhr = new XMLHttpRequest()
xhr.open('GET', 'http://127.0.0.1:3000/cors', true)
xhr.send()
xhr.addEventListener('load', (data) => {
if (xhr.status === 200) {
const resData = data.target.response
dataShow.innerHTML = resData
}
})
})
</script>
</body>
</html>
服务端代码
const express = require('express')
const router = express.Router()
const Mock = require('mockjs')
router.use('/', (req, res, next) => {
console.log('cors')
res.append('access-control-allow-origin', 'http://127.0.0.1:8090')
// res.append('withCredentials', true)
next()
})
router.get('/', (req, res, next) => {
const data = Mock.mock({
'list|1-10': [{
'id|+1': 1,
'name|1-3': '@FIRST'
}]
})
res.json(data)
})
module.exports = router
实际演示
CORS 演示3. 降域
假设现在我有两个域名 a.sub.com 和 b.sub.com,但实际上指向的是同一个 ip 地址和 端口,尽管如此,因为域名不同,依旧是非同源,为了解决这个问题,通过window.domain
来降域,解决跨域问题。
修改 hosts 文件
修改 hosts
两个 html 文件,里边使用 iframe 演示。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>网站 a</title>
<style>
iframe {
background-color: #eee;
}
</style>
</head>
<body>
<h1>使用降域实现跨域</h1>
<input type="text" placeholder="http://a.sub.com:8090/a.html">
<iframe src="http://b.sub.com:8090/b.html" frameborder="0"></iframe>
<script>
document.domain = 'sub.com'
</script>
</body>
</html>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>网站 b.com</title>
</head>
<body>
<h1>这是网站 b</h1>
<script>
document.domain = 'sub.com'
</script>
</body>
</html>
在未使用window.domain
降域之前,在网站 a 里是无法访问网站 b 的节点的,如下:
而加上之后:
降域成功演示
网友评论