前言:
由于浏览器为了防止恶意网站窃取用户在其他网站的敏感信息,如用户登录状态、个人数据等。于是浏览器就会开启同源策略。 同源策略:它限制一个源的文档或者加载的 JS 脚本,与另外一个源的资源进行交互,帮助我们阻隔恶意文档,减少被攻击的媒介
同源策略
- 一个 "源" 是由协议(scheme)、主机(host)和端口(port)来定义的。只有当这三个部分完全相同的时候,两个网页才被认为是同源的。
- 是浏览器的一种安全机制,它规定了只有来自相同 "源" 的脚本才能访问当前网页的文档对象模型(DOM)、本地存储(
localStorage
等)、会话存储(sessionStorage
)、Cookies
以及其他敏感资源。
解决跨域
跨域的本质就是使得浏览器不触发同源策略
JSONP
使用JSONP方式实质上是借用了浏览器的一种取巧方式实现,它可能看起来不怎么优雅。由于浏览器在对包括img
、script
、radio
等html标签发送的网络请求并不会触发浏览器使用同源策略的,于是我们便可以利用这个漏洞实现跨域问题。
首先我们创建一个server.js,使用node在3000端口处打开服务器
js
const http = require('http')
http.createServer((req, res) => {
res.end('success')
})
.listen(3000)
其次创建index.html,将监听的地址放入script
的src中
js
function JSON(url) {
return new Promise((resolve, reject) => {
const script = document.createElement('script')
script.src = url
document.body.appendChild(script)
})
}
JSON('http://localhost:3000')
.then(res => {
console.log(res);
})
我们封装了一个JSONP函数,为了拿到结果的异步操作,使得该函数返回一个Promise
对象。但很快我们就会发现一个问题,我们根本没有拿到返回的数据显示的是该数据success
没有被定义,并且正常的GET
请求是能够传递params
参数。
转换一个思路,如果我们通过window
全局去触发这个函数呢?在加载全局的时候直接将该方法挂载上去,在服务器响应回来拿到数据的时候,直接将挂载在window
上的方法触发,并且通过url
传递我们需要的params
参数。
js
function JSON(url, callback) {
return new Promise((resolve, reject) => {
const script = document.createElement('script')
window[callback] = function (data) {
resolve(data)
}
script.src = `${url}?callback=${callback}`
document.body.appendChild(script)
})
}
JSON('http://localhost:3000', 'callback')
.then(res => {
console.log(res);
})
同时后端那边也需要协助我们完成这一次操作,后端需要再拿到url
的请求后,处理分离出函数名,并将后端的实参传递到挂载在window
的函数上面。
js
const http = require('http')
http.createServer((req, res) => {
const query = new URL(req.url, `http://${req.headers.host}`).searchParams
if (query.get('callback')) {
const callback = query.get('callback')
const data = '我是数据'
const result = `${callback}("${data}")`
res.end(result)
}
res.end('success')
})
.listen(3000)
值得一提的是JSONP方式只能过去处理GET
请求,这是由于script
标签本身只能发送GET
请求,从而限制了JSONP方式只能处理该请求
WebSocket
使用WebSocket
协议,WebSocket
连接建立时会使用 HTTP 协议进行握手。客户端发起一个特殊的 HTTP 请求,这个请求中包含一些特殊的头部信息,用于表明这是一个 WebSocket
升级请求。
WebSocket
是全双工协议,一旦连接建立,服务器和客户端可以在这个连接上双向自由地发送数据,不需要像 HTTP 那样每次重新建立连接来进行通信。
首先建立后端的服务器
js
const WebSocket = require('ws')
const ws = new WebSocket.Server({ port: 3000 })
ws.on('connection', (obj) => {
obj.on('message', (data) => {
obj.send('我是 WebSocket')
})
})
建立连接请求
js
function web_Socket(url) {
return new Promise((resolve) => {
const socket = new WebSocket(url)
socket.onopen = () => {
socket.send('http://localhost:3000')
}
socket.onmessage = (res) => {
resolve(res.data)
}
})
}
web_Socket('http://localhost:3000').then(data => {
console.log(data);
})
CORS
采用 CORS
(跨域资源共享),一种基于 HTTP 头的机制,该机制通过允许服务器标示除了它自己以外的其他源(域、协议或端口),来访问加载服务器上的资源。
其实际上就是通过添加在响应头中的特殊字段,使得浏览器不会使用同源策略对响应资源进行干扰。
后端建立服务器同时设置允许的源,当然我们也能设置成'Access-Control-Allow-Origin': '*'
这样会允许所有的源对服务器进行访问,我们可以设置自己响应的源进行访问。
js
const http = require('http');
http.createServer(function (req, res) {
res.writeHead(200, {
'Access-Control-Allow-Origin': 'http://127.0.0.1:5500'
})
res.end('hello world')
}).listen(3000);
前端通过ajax发送请求
js
const xhr = new XMLHttpRequest();
xhr.open('GET', 'http://localhost:3000')
xhr.send();
xhr.onreadystatechange = function () {
if (xhr.readyState === 4 && xhr.status === 200) {
console.log(xhr.responseText);
}
}
总结
总而言之,所有的跨域解决方式都是需要通过后端进行特殊的处理操作,或者是前后端一起对接工作进行特殊的处理。当然解决跨域的方式远不止这些,还有诸如postMessage、nginx等方式能解决跨域问题,大家可以自行进行学习拓展哦!