目录
1、浏览器的同源策略
* 两个URL的协议、主机、端口都相同的话,则这两个URL同源
* 浏览器的cookie、sessionStorage、localStorage、iframe、ajax都不支持跨域
* 跨域方案
- jsonp
- cors
- postMessage
- document.domain
- window.name
- location.hash
- http-proxy
- nginx
- websocket
2、jsonp
* 原理
- 浏览器的link、script、img支持跨域
- 后端返回的是一段js脚本
* 缺点
- 只能发送get请求,不支持post、put、delete
- 跨站脚本攻击(xss),不安全
jsonpUtil({
url: 'http://localhost:3000/world',
params: {name: '发条'},
cb: 'callback',
}).then(res => {
console.log(res)
})
// 后端返回字符串:callback({name: '发条技师', age: 23})
function jsonpUtil({url, params, cb}) {
return new Promise(resolve => {
const scriptEl = document.createElement('script')
params.cb = cb
const paramsFormat = Object.keys(params).map(key => `${key}=${params[key]}`).join('&')
scriptEl.setAttribute('src', url + '?' + paramsFormat)
document.head.appendChild(scriptEl)
window[cb] = function (res) {
document.head.removeChild(scriptEl)
window[cb] = null
resolve(res)
}
})
}
3、cors
const express = require('express')
const app = express()
app.use(function (req, res, next) {
// 访问控制允许的源,*是通配符
res.setHeader('Access-Control-Allow-Origin', req.headers.origin)
// 访问控制允许的请求头
res.setHeader('Access-Control-Allow-Headers', 'Authorization')
// 访问控制允许的请求方式
res.setHeader('Access-Control-Allow-Methods', 'PUT')
// 访问控制预检(OPTIONS请求)间隔(单位秒)
res.setHeader('Access-Control-Max-Age', 60)
// 访问控制允许cookie
res.setHeader('Access-Control-Allow-Credentials', true)
// 访问控制暴露的请求头
res.setHeader('Access-Control-Expose-Headers', 'skill')
next()
})
app.get('/world', function (req, res) {
res.end(`callback({name: '冰霜巨龙', age: 28})`)
})
app.put('/hello', function (req, res) {
console.log(req.headers.cookie)
res.setHeader('skill', '300')
res.end(`callback({name: '发条技师', age: 23})`)
})
app.listen(3000)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>前台页面</title>
<script src="jquery.min.js"></script>
</head>
<body>
<script>
document.cookie = 'hero=sv;path=/'
$.ajax({
url: 'http://localhost:3000/hello?cb=callback',
type: 'PUT',
headers: {
Authorization: 'Bearer xxx',
},
xhrFields: {
// 请求携带cookie
withCredentials: true
},
success(data, textStatus, xhr) {
console.log(xhr.getAllResponseHeaders())
}
})
</script>
</body>
</html>
4、postMessage
const express = require('express')
const path = require('path')
const app = express()
app.use(express.static(path.resolve(__dirname, '../static')))
app.listen(3000)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>a</title>
</head>
<body>
<button onclick="handleClick()">a发送</button>
<iframe id="iframeElId" src="http://localhost:4000/b.html"></iframe>
<script>
const iframeEl = document.getElementById('iframeElId')
window.onmessage = function (ev) {
console.log(ev.data)
}
function handleClick() {
iframeEl.contentWindow.postMessage('白牛', 'http://localhost:4000')
}
</script>
</body>
</html>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>b</title>
</head>
<body>
<button onclick="handleClick()">b发送</button>
<script>
let source = null
window.onmessage = function (ev) {
console.log(ev.data)
source = ev.source;
}
function handleClick() {
source.postMessage('小牛', '*')
}
</script>
</body>
</html>
5、window.name
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>a</title>
</head>
<body>
<!-- a和c不同域 -->
<iframe src="http://localhost:4000/c.html" onload="handleLoad(this)"></iframe>
<script>
let first = true
function handleLoad(self) {
if (first) {
// a和b同域
self.setAttribute('src', 'http://localhost:3000/b.html')
first = false
} else {
console.log(self.contentWindow.name)
}
}
</script>
</body>
</html>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>b</title>
</head>
<body>
</body>
</html>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>c</title>
</head>
<body>
<script>
window.name = '敌法师'
</script>
</body>
</html>
6、location.hash
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>a</title>
</head>
<body>
<!-- a和c不同域 -->
<iframe src="http://localhost:4000/c.html#pa"></iframe>
<script>
window.onhashchange = function () {
console.log(location.hash)
}
</script>
</body>
</html>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>b</title>
</head>
<body>
<script>
// a和b同域
window.parent.parent.location.hash = location.hash
</script>
</body>
</html>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>c</title>
</head>
<body>
<script>
console.log(location.hash)
const iframeEl = document.createElement('iframe');
iframeEl.setAttribute('src', 'http://localhost:3000/b.html#ug')
document.body.appendChild(iframeEl)
</script>
</body>
</html>
7、document.domain
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>a</title>
</head>
<body>
<!--
* hosts文件配置
- 127.0.0.1 www.linjincheng.com
- 127.0.0.1 blog.linjincheng.com
-->
<iframe src="http://blog.linjincheng.com:3000/b.html" onload="handleLoad(this)"></iframe>
<script>
document.domain = 'linjincheng.com'
function handleLoad(el) {
console.log(el.contentWindow.info)
}
</script>
</body>
</html>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>b</title>
</head>
<body>
<script>
document.domain = 'linjincheng.com'
window.info = '屠夫'
</script>
</body>
</html>
8、websocket
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>a</title>
</head>
<body>
<script>
const socket = new WebSocket('ws://localhost:3000')
socket.onopen = function () {
socket.send('萨尔')
}
socket.onmessage = function (ev) {
console.log(ev.data)
}
</script>
</body>
</html>
const {WebSocketServer} = require('ws')
const wss = new WebSocketServer({port: 3000})
wss.on('connection', function (ws) {
ws.on('message', function (data) {
console.log(data.toString())
ws.send('希尔瓦娜斯')
})
})
9、nginx
http {
server {
location ~.*\.json {
root json;
add_header "Access-Control-Allow-Origin" "*";
}
}
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>a</title>
<script src="jquery.min.js"></script>
</head>
<body>
<script>
$.ajax({
url: 'http://localhost:3000/info.json',
success(data) {
console.log(data)
}
})
</script>
</body>
</html>