Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

跨域详解及其常见的解决方式 #1

Open
yangrenmu opened this issue Mar 3, 2019 · 0 comments
Open

跨域详解及其常见的解决方式 #1

yangrenmu opened this issue Mar 3, 2019 · 0 comments

Comments

@yangrenmu
Copy link
Owner

yangrenmu commented Mar 3, 2019

跨域是什么

跨域是一个域下的网页去请求另一个域下的资源。严格点来说就是两个域的协议、域名、端口任何一个不同时,都会被当作跨域。当跨域访问资源时,会受到浏览器的安全限制,详细的情况可以看下表:

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.comcookieevil.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\n')
      }
    });
    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

    原理:WebSocketHTML5的协议,可以让浏览器与服务器之间建立一个全双工、双向通信。当浏览器使用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 发送给客户端的数据
    })
    
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant