跨域是什么
跨域是一个域下的网页去请求另一个域下的资源。严格点来说就是两个域的协议、域名、端口任何一个不同时,都会被当作跨域。当跨域访问资源时,会受到浏览器的安全限制,详细的情况可以看下表:
URL | 说明 | 是否允许通信 |
---|---|---|
http://www.a.com/a.js http://www.a.com/b.js |
同一域名 | 允许 |
http://www.a.com/a/a.js http://www.a.com/b/b.js |
同一域名,不同文件夹 | 允许 |
http://www.a.com:3000/a.js http://www.a.com/b.js |
同一域名,不同端口 | 不允许 |
http://www.a.com/a.js https://www.a.com/b.js |
同一域名,不同协议 | 不允许 |
http://www.a.com/a.js http://70.32.92.74/b.js |
域名和域名对应IP | 不允许 |
http://www.a.com/a.js http://script.a.com/b.js |
主域相同,子域不同 | 不允许 |
http://www.a.com/a.js http://a.com/b.js |
同一域名,不同二级域名(同上) | 不允许 |
http://www.cnblogs.com/a.js http://www.a.com/b.js |
不同域名 | 不允许 |
为什么限制跨域访问资源
浏览器限制跨域访问资源是一种安全策略,可以预防某些恶意行为。浏览器在每次发起请求时都会带上cookie
,试想下,如果没有这总安全策略,evil.com
也会拿到用户在secure.com
的cookie
,evil.com
利用cookie
里的信息登录用户的账号,这样用户的数据就被泄露了。跨域限制就是为了避免这种情况的发生。
跨域资源的几种访问方式
-
jsonp
原理:
jsonp
之所以能够实现跨域资源的访问,是因为<script>
标签不受浏览器同源策略的限制,使用时将src
属性指定一个跨域URL
,服务器在收到请求后,将数据放到指定的callback
里传回来。实现:
前端部分: function fetchjsonp(res) { console.log(res) // 可以获得服务端的数据,{data: "json data"} } const script = document.createElement('script') script.src = 'http://127.0.0.1:8080?callback=fetchjsonp' document.head.appendChild(script) 服务端: const http = require('http'); const hostname = '127.0.0.1'; const port = 8080; const server = http.createServer((req, res) => { if (~req.url.indexOf('?callback')) { // 简单处理 JSONP 跨域的时候 const obj = { "data": 'json data', } const callback = req.url.split('callback=')[1] const jsonData = callback + `(${JSON.stringify(obj)})` res.end(jsonData) // 这里最终返回前端的是相当于调用函数 callback({json}) } else { // 非跨域的时候 res.statusCode = 200 res.setHeader('Content-Type', 'text/plain') res.end('not jsonp ') } }); server.listen(port, hostname, () => { console.log(`Server running at http://${hostname}:${port}/`); });
优缺点:
jsonp
优点是兼容性好,支持低版本的浏览器跨域访问。缺点是只支持get
请求,不容易判断请求是否失败。 -
CORS
原理:CORS(cross-origin-resource-sharing)跨域资源共享,其思想是使用自定义的
HTTP
头部,让浏览器域服务器进行沟通,从而决定请求或响应是成功还是失败。服务器端一般在Access-Control-Allow-Origin
中指定对应的域,当浏览器访问对应的资源。
实现:前端部分: axios.get('http://127.0.0.1:8080/').then(res => { console.log(res) // data from cors }) 服务端: const http = require('http') const hostname = '127.0.0.1' const port = 8080 const server = http.createServer((req, res) => { res.setHeader('Access-Control-Allow-Origin', 'http://127.0.0.1:3000') // 设置请求源 res.setHeader('Access-Control-Allow-Methods', 'get') // 设置请求方法 res.end('data from cors') }) server.listen(port, hostname, () => { console.log(`Server running at http://${hostname}:${port}/`) })
优缺点:优点是支持所有的
HTTP
请求方法,缺点是不支持老的浏览器; -
使用代理
原理:上面我们已经说了,浏览器的跨域限制是发生在浏览器里的,服务器端是没有的,所以可以使用服务器代理请求其他域的资源,然后在返回给客户端。
实现:比如说浏览器直接访问
http://127.0.0.1:8080
会被限制,那么我们可以使用node作代理,来获取http://127.0.0.1:8080
返回的资源。前端部分: axios.get('http://127.0.0.1:8000/').then(res => { console.log(res) }) node代理: const express = require('express') const request = require('request') const app = express() app.use('/', function (req, res) { res.set('Access-Control-Allow-Origin', '*') const url = 'http://127.0.0.1:8080' // 代理的URL req.pipe(request(url)).pipe(res) }) app.listen(process.env.PORT || 8000)
-
使用WebSocket
原理:
WebSocket
是HTML5
的协议,可以让浏览器与服务器之间建立一个全双工、双向通信。当浏览器使用websocket
与服务器建立连接后,HTTP
协议会变成websocket
协议,websocket
协议是不受同源策略限制的,所以可以实现跨域请求资源;实现:
前端部分: const ws = new WebSocket("ws://127.0.0.1:8080") ws.onopen = function (e) { console.log('Connection to server opened') } ws.onmessage = function (event) { console.log('Client received a message: ', event.data) // 客户端接受的数据在 event.data 中 } 服务端: const WebSocketServer = require('ws').Server const ws = new WebSocketServer({ port: 8080 }) ws.on('connection', (ws) => { ws.send('hello websocket') // websocket 发送给客户端的数据 })