跨域及解决方案

本文详细介绍了跨域的概念、同源策略及其目的,列举了四种跨域情况,并探讨了浏览器的访问限制。针对跨域问题,文章详细讲解了JSONP的工作原理和代码实现,CORS的机制、预检请求以及如何处理Cookie。最后,提出了使用代理(如webpack代理和nginx反向代理)解决跨域的方法。总结了CORS和JSONP的优缺点,并指出代理主要通过不受同源策略限制的服务端来实现跨域。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、什么是跨域

1. 同源策略

  • 跨域是指一个源下的文档或脚本想要去请求另一个源下的资源。

  • 非同源请求,也就是协议、端口、主机其中一项不相同的时候。

  • 比如:<link><script><img>等Dom标签,还有在样式中嵌入的文件外链,以及JS发起的ajax请求等都存在跨域。

2. 目的

  • 主要是用来防止 CSRF 攻击的。简单点说,CSRF 攻击是利用用户的登录态发起恶意请求

3. 哪些不会被跨域限制?

  • 页面中的链接,重定向以及表单提交是不会受到同源策略限制的。
  • 我们还发现凡是拥有”src”这个属性的标签都拥有跨域的能力,比如<\script>、<\img>、<\iframe>

三、4 种跨域情况

当协议、子域名、主域名、端口号中任意一个不相同时,都算作不同域

  1. 不同域名属于跨域,如:www.a.com 和www.b.com另外www.a.com 和www.a.com.cn也属于不同域名。
  2. 主域名和子域名(二级域名、三级域名等)跨域,如:www.a.combbs.a.com 跨域。
  3. 不同协议属于跨域,如:http://www.a.com 和 https://www.a.com。
  4. Ip和域名属于跨域,如:123.23.23.12 和 www.a.com

四、访问限制

除了跨域请求之外,还有以下限制

储存

  • 当前域下的 js 脚本不能够访问其他域下的 cookie、localStorage 和 indexDB。浏览器中存储用户数据时,会标记数据的来源(origin),当JS脚本访问本地数据时,会检查它的orign是否一致,不一致就拒绝访问。

操作DOM

  • 当前域下的 js 脚本不能够操作访问操作其他域下的 DOM。

五、跨域解决方案

1. JSONP

(1)简介

JSONP全称叫JSON with Padding,将数据填充进回调函数,是一个非官方的协议

(2)原理

简单来说,就是利用<script> 标签没有跨域限制

客户端

(请求地址、参数(包含回调函数名)、全局函数(接收数据))

  • 全局定义一个函数A,用于处理响应数据。(script)
  • script标签的src属性中,设置请求地址参数,将函数A的函数名作为参数callback的值进行传递。

服务端

  • 收到请求后,返回一个函数A的调用,将响应数据传入函数A中。

结果

客户端接收到响应就相当于:

  • 页面中定义函数A,对传入的参数进行处理。
  • 用来发送请求是script相当于:调用函数A,将响应数据作为函数的参数。

注意:请求中callback这个键名需要和后端约定好。

**(3)代码演示 **

客户端

<script>
    var script = document.createElement('script');
    script.type = 'text/javascript';
    // 传参一个回调函数名给后端,方便后端返回时执行这个在前端定义的回调函数
    script.src = 'http://www.domain2.com:8080/login?user=admin&callback=handleCallback';
    document.head.appendChild(script);
    // 回调执行函数
    function handleCallback(res) {
        alert(JSON.stringify(res));
    }
 </script>

服务端返回内容

handleCallback({"success": true, "user": "admin"})

(4)特点

  • 具有局限性, 仅支持get方法
  • 不安全,可能会遭受XSS攻击

2. CORS

(1)简介

  • CORS是一个W3C标准,全称是跨域资源共享,它允许浏览器发出跨域请求。
  • 它需要浏览器和后端同时支持。目前所有浏览器都支持CORS 通信(IE浏览器不低于IE10),实现 CORS 通信的关键是后端
  • 简单来说,如果遇到跨域请求,浏览器能够发出请求,但是需要服务端支持,如果服务端不支持,返回的内容就会被浏览器拦截。

(2)实现

  • 服务端设置 Access-Control-Allow-Origin 就可以开启 CORS。 该属性表示哪些域名可以访问资源,如果设置通配符则表示所有网站都可以访问资源。

(3)简单请求

请求过程

  • 对于简单请求,浏览器会直接发出CORS请求,它会在请求的头信息中增加一个Origin字段,该字段用来说明本次请求来自哪个源(协议+端口+域名).
  • 服务器会根据这个值来决定是否同意这次请求。如果在允许范围内,响应信息中额外携带 Access-Control-Allow-Origin字段,如果不再允许范围内,就没有这个头部信息。浏览器根据这个头部来判断是否要展示数据。如果没有这个响应头部信息,数据就会被拦截。

使用以下请求方法属于简单请求

  • GET
  • HEAD
  • POST

满足简单请求条件的还有很多情况,这里不一一列举。

(4)复杂请求

  • 非简单请求是对服务器有特殊要求的请求,比如请求方法为DELETE或者PUT等。

  • 非简单请求的CORS请求会在正式通信之前进行一次HTTP查询请求,称为预检请求

  • 在这个预检请求,是浏览器自动发出,询问服务器当前所在的网页是否在服务器允许访问的范围内,以及可以使用哪些HTTP请求方式和头信息字段,只有得到肯定的回复,才会进行正式的HTTP请求。(否则就会报错)

  • 预检请求使用的请求方法是OPTIONS,表示这个请求是来询问的。他的头信息中的关键字段是Origin,表示请求来自哪个源。除此之外,头信息中还包括请求方法和会额外发送的头信息。(两个字段:

    • Access-Control-Request-Method:该字段是必须的,用来列出浏览器的CORS请求会用到哪些HTTP方法。
    • Access-Control-Request-Headers: 该字段是一个逗号分隔的字符串,指定浏览器CORS请求会额外发送的头信息字段。)
  • 服务器在收到浏览器的预检请求之后,会根据请求头部(源、请求方式等)进行判断,只有服务端返回允许请求的响应,浏览器才会发送正式的请求,否则报错。

(5)减少OPTIONS请求

  • OPTIONS请求次数过多就会损耗页面加载的性能,降低用户体验度。可以缓存预检结果。
  • 通过响应头来设置预检请求结果的缓存时间
  • 要减少OPTIONS请求次数,可以后端在请求的返回头部添加:Access-Control-Max-Age:number。它表示预检请求的返回结果可以被缓存多久,单位是秒。
  • 该字段只对完全一样的URL的缓存设置生效,所以设置了缓存时间,在这个时间范围内,再次发送请求就不需要进行预检请求了。

(6)Cookie

  • 默认情况下在CORS请求是不带 cookie 的。
  • 需要在服务端和客户端都进行设置同意。
  • 在客户端,是在代码中设置 XMLHttpRequest 的 withCrendetails 属性
  • 服务端进行相应的设置就可以发送cookie了。

(7)优缺点

优点

  1. 支持所有http请求
  2. 安全性比JSONP高
  3. 配置简单

缺点

  1. 不兼容老版本浏览器(IE浏览器不低于IE10)
  2. 需要服务端支持

(8)浏览器拦截

跨域请求发出:同CORS请求(有简单请求和复杂请求)

浏览器对跨域拦截:

  1. 如果响应头中没有允许跨域访问的配置,则不加载,并报出响应异常
  2. 如果响应头中有允许跨域访问的设置,则正常加载数据。

3. Proxy

  • 代理(Proxy)也称网络代理,是一种特殊的网络服务,允许一个(一般为客户端)通过这个服务与另一个网络终端(一般为服务器)进行非直接的连接。
  • 同源策略是浏览器的限制,服务端没有这个限制,也就是说从一个服务端向另一个服务端发起请求并无跨域。
  • 一般认为代理服务有利于保障网络终端的隐私或安全,防止攻击。

方案一

  • 如果是通过vue-cli脚手架工具搭建项目,我们可以通过webpack为我们起一个本地服务器作为请求的代理对象

  • 通过该服务器转发请求至目标服务器,得到结果再转发给前端,但是最终发布上线时如果web应用和接口服务器不在一起仍会跨域

    vue.config.js文件,新增以下代码

    amodule.exports = {
        devServer: {
            host: '127.0.0.1',
            port: 8084,
            open: true,// vue项目启动时自动打开浏览器
            proxy: {
                '/api': { // '/api'是代理标识,用于告诉node,url前面是/api的就是使用代理的
                    target: "http://xxx.xxx.xx.xx:8080", //目标地址,一般是指后台服务器地址
                    changeOrigin: true, //是否跨域
                    pathRewrite: { // pathRewrite 的作用是把实际Request Url中的'/api'用""代替
                        '^/api': "" 
                    }
                }
            }
        }
    }
    

    通过axios发送请求中,配置请求的根路径

    axios.defaults.baseURL = '/api'
    

方案二

  • 通过node服务端实现代理请求转发,以express框架为例

    var express = require('express');
    const proxy = require('http-proxy-middleware')
    const app = express()
    app.use(express.static(__dirname + '/'))
    app.use('/api', proxy({ target: 'http://localhost:4000', changeOrigin: false
                          }));
    module.exports = app
    

方案三

  • 通过配置nginx实现代理

    server {
        listen    80;
        # server_name www.josephxia.com;
        location / {
            root  /var/www/html;
            index  index.html index.htm;
            try_files $uri $uri/ /index.html;
        }
        location /api {
            proxy_pass  http://127.0.0.1:3000;
            proxy_redirect   off;
            proxy_set_header  Host       $host;
            proxy_set_header  X-Real-IP     $remote_addr;
            proxy_set_header  X-Forwarded-For  $proxy_add_x_forwarded_for;
        }
    }
    

4. 总结

  1. CORS支持所有类型的HTTP请求,是跨域HTTP请求的根本解决方案JSONP只支持GET请求
  2. JSONP的优势在于支持老式浏览器,以及可以向不支持CORS的网站请求数据。
  3. 不管是Node中间件代理还是nginx反向代理,主要是通过同源策略对服务器不加限制。
  4. 日常工作中,用得比较多的跨域方案是CORSNginx反向代理
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值