• 跨域


    同源策略

    含义

    A 网页设置的Cookie,B网页不能打开,除非这两个网页同源。所谓同源是指

    • 协议相同
    • 域名相同
    • 端口相同

    举个来说,http://www.abc.com/index这个网址,协议是http://,域名是www.abc.com,端口是80(默认端口可以省略)

    • http://www.abc.com/index2 : 同源
    • https://www.abc.com/index : 不同源(协议不同)
    • http://www.abcde.com/index :不同源(域名不同)
    • http://www.abc.com:81/index :不同源(端口不同)

    目的

    同源策略的目的,是为了保证用户的安全,防止恶意的网站窃取数据。

    限制范围

    目前如果非同源,共有三种行为收到限制。

    • CookieLocalStorageIndexDB 无法读取
    • DOM 无法获得
    • AJAX请求不能发送

    跨域请求的安全问题

    ​ 通常,浏览器会对上面提到的跨域请求作出限制。浏览器之所以要对跨域请求作出限制,是出于安全方面的考虑,因为跨域请求有可能被不法分子利用来发动 CSRF攻击。

    CSRF攻击:
    CSRF(Cross-site request forgery),中文名称:跨站请求伪造,也被成为:one click attack/session riding,缩写为:CSRF/XSRF。CSRF攻击者在用户已经登录目标网站之后,诱使用户访问一个攻击页面,利用目标网站对用户的信任,以用户身份在攻击页面对目标网站发起伪造用户操作的请求,达到攻击目的。

    CSRF 攻击的原理大致描述如下:有两个网站,其中A网站是真实受信任的网站,而B网站是危险网站。在用户登录了收信人的A网站时,本地会存储A网站相关的Cookie,并且浏览器也维护这一个Session会话。这时,如果用户在没有登出A网站的情况下访问危险网站B,那么危险网站B就可以模拟发出一个对A网站的请求(跨域请求)对A网站进行操作,而在A网站的角度来看是并不知道请求是由B网站发出来的(Session和Cookie均为A网站的),这时便成功发动一次CSRF 攻击。

    因此,大多数浏览器都会跨域请求作出限制,这是从浏览器层面上的对 CSRF 攻击的一种防御,但是需要注意的是在复杂的网络环境中借助浏览器来防御 CSRF 攻击并不足够,还需要从服务端或者客户端方面入手防御。详细可以参考这篇文章浅谈CSRF攻击方式

    跨域正确打开方式

    服务端

    const Koa = require('koa'); 
    const route = require('koa-route');
    const app = new Koa();
    const fs = require('fs');
    const main = async ctx=>{
            ctx.response.body = await fs.createReadStream('./1.json')
    }
    
    const add = async ctx=>{
        await ctx.req.addListener('data', (data) => { // 有数据传入的时候
            console.log(data,'aa')
        });
    }
    app.use(route.get('/',main))
    app.use(route.post('/add'),add)
    app.listen(3300)
    

    上面代码可以看到我们的服务端口是3300

    客户端

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <meta http-equiv="X-UA-Compatible" content="ie=edge">
        <title>Document</title>
    </head>
    <body>
        <div id="div">
            
        </div>
        <button class="button">---</button>
        <button class="add">++</button>
    </body>
    </html>
    <script src="./node_modules/axios/dist/axios.min.js"> </script>
    <script>
        let div = document.getElementById('div')
        let button = document.getElementsByClassName('button')[0];
        let add = document.getElementsByClassName('add')[0];
        let fn = ()=>{
            axios.get('http://192.168.10.40:3300/').then(res=>{
            let {data} = res;
            let str = ``;
            data.forEach(item=>{
                str+=`<p style="color:'red';fonst-size:'30px'">${item.title}</p><p style="color:'aqua'">${item.content}</p>`
            })
            div.innerHTML = str;
        })
        }
        fn()
        button.onclick = function(){
            let index = 1 
            axios.get(`http://192.168.10.40:3300/delete?index=${index}`).then(res=>{
                console.log(res,'aaa')
                let {data} = res;
                data == 1&& fn()
            })
        }
    </script>
    

    当我们执行这段代码时,控制台会报错Access to XMLHttpRequest at 'http://---:3300/' from origin 'http://---:5500' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.,因为两个地址的端口不一样,所以会造成跨域

    这个错误就是提示跨域

    解决跨域的方式

    jsonp

    jsonp是一种跨域通信的手段,特德原理其实很简单:

    • 首先是利用script标签的src属性来实现跨域。
    • 通过将前端方法作为参数传递到服务器端,然后由服务器端注入参数之后在返回,实现服务端向客户端通信。
    • 由于使用script 标签的src属性,因此只支持get方法

    服务端

    const Koa = require('koa'); 
    const route = require('koa-route');
    const app = new Koa();
    const fs = require('fs');
    const main = async ctx=>{
        let _data = await fs.readFileSync('./1.json','utf-8')
        let {query} = ctx.request;
        ctx.body = `${query.callback}(${JSON.stringify(_data)})`
    }
    
    const add = async ctx=>{
        await ctx.req.addListener('data', (data) => { // 有数据传入的时候
            console.log(data,'aa')
        });
    }
    app.use(route.get('/',main))
    app.use(route.post('/add'),add)
    app.listen(3300)
    

    客户端

        function jsonp({url,params,callback}) {
            return new Promise((resolve,reject)=>{
                let _script = document.createElement('script');
                window[callback] = function(data) {
                    resolve(JSON.parse(data))
                }
                params = {...params,callback};
                let _arr = [];
                for(let key in params){
                    // 属性值与属性名以等号方式拼接在一起
                    _arr.push(`${key}=${params[key]}`)
                }
                _script.src = `${url}?${_arr.join('&')}`;
                document.body.appendChild(_script);
                
            })
        }
        jsonp({
            url:'http://192.168.10.40:3300/',
            params:{},
            callback:'show'
        }).then(res=>{
            console.log(res,'数据')
        })
    

    cors

    cors 与 jsonp的区别

    • jsonp只能实现get请求,而cors支持所有类习惯的HTTP请求.
    • 使用cors,开发者可以使用普通的XMLHttpRequest发起请求和获取数据,比起jsonp有更好的错误处理。

    cors对于浏览器发过来的AJAX跨域七个球分为两种

    • 简单请求
    • 非简单请求
    简单请求

    所谓简单请求就是HEAD,get,post三种请求方式

    浏览器会在header信息里卖弄多加一个字段:

    key:origin
    value: 协议+域名+端口
    

    服务器根据对象里的origin值来决定是否同意这次请求

    如果请求通过

    请求通过后,服务器会在header里多增加几个字段:

    Access-Control-Allow-Origin: https://localhost:8080
    Access-Control-Allow-Credentials: true
    Access-Control-Expose-Headers: Token
    

    上面的头信息之中,有三个与cors请求相关的字段,都以Access-Control-开头。

    1. Access-Control-Allow-Origin:
      该字段是必须的。他的值要么是请求时origin字段的值,要么时一个*,标识接收任意域名的请求
    2. Access-Control-Allow-Credentials:
      该字段可选。他的值是一个布尔值,标识是否允许发送Cookie。默认情况下,Cookie不包括在CORS请求之中。设为true,即表示服务器明确许可,Cookie可以包含在请求中,一起发给服务器。这个值也只能设为true,如果服务武器不要浏览器发送Cookie,删除该字段即可
    3. Access-Control-Expose-Headers:该字段可选。cors请求时,XMLHttpRequest对象的getResponseHeader()方法只能拿到6个基本字段:Cache-ControlContent-LanguageContent-TypeExpiresLast-ModifiedPragma。如果想拿到其他字段,就必须在Access-Control-Expose-Headers里面制定。
    非简单请求

    非简单请求是那种对服务器有特殊要求的请求,比如请求方法是PUTDELETE,或者Content-Type字段的类型是application/json

    非简单请求的CORS请求,会在 证是通信之前,增加一次HTTP查询请求,成为预检请求

        axios.put('http://192.168.10.40:3300/').then(res=>{
            
        })
    

    上面代码中,HTTP请求的方法是PUT

    浏览器发现,这是一个非简单请求,就自动发出一个"预检"请求,要求服务器确认可以这样请求。下面是这个"预检"请求的信息

    "预检"请求用的请求方法是OPTTIONS,标识这个请求时用来查询的。头信息里面,关键字段时origin,标识请求来自哪个源。除了origin字段,"预检"请求的头信息包括两个特殊的字段

    1. Access-Control-Request-Method:该字段时必须的,用来列出浏览器的CORS请求会用到那些HTTP方法,上列是PUT
    2. Access-Control-Request-Headers: 该字段是一个逗号分隔的字符串,指定浏览器CORS请求会额外发送的头信息字段。
    预检请求的回应

    浏览器收到"预检"请求以后,检查了originAccess-Control-Request-MethodAccess-Control-Request-Headers字段以后,确认允许跨域请求,就可以做出回应。

    HTTP/1.1 200 OK
    Date: Mon, 01 Dec 2008 01:15:39 GMT
    Server: Apache/2.0.61 (Unix)
    Access-Control-Allow-Origin: http://api.bob.com
    Access-Control-Allow-Methods: GET, POST, PUT
    Access-Control-Allow-Headers: X-Custom-Header
    Content-Type: text/html; charset=utf-8
    Content-Encoding: gzip
    Content-Length: 0
    Keep-Alive: timeout=2, max=100
    Connection: Keep-Alive
    Content-Type: text/plain
    

    上面的HTTP回应中,关键的是Access-Control-Allow-Origin字段,表示http://api.bob.com可以请求数据。该字段也可以设为*,表示统一任意跨源请求。

    Access-Control-Allow-Origin: *
    

    如果浏览器否定了"预检"请求,会返回一个正常的HTTP回应,但是没有任何CORS相关的头信息字段。这时,浏览器就会认定,服务器不同意预检请求,因此触发一个错误。

    Access-Control-Allow-Methods

    该地段必须,它的值是逗号分隔的一个字符串,表名服务器支持的所有跨域请求的方法。注意,返回的是所有支持的方法。注意,返回的是所有支持的方法,而不单是浏览器请求的那个方法。这是为了避免多次"预检"请求。

    Access-Control-Allow-Headers

    如果浏览器请求包括Access-Control-Request-Headers字段,则Access-Control-Allow-Headers地段是必须的。它也是一个逗号分隔的字符串,表明服务器支持的所有头信息字段,不限于浏览器在"预检"中请求的字段

    Access-Control-Allow-Credentials

    与简单请求时的含义相同

    Access-Control-Max-Age

    该字段可选,用来制定本次预检请求的有效期,单位为秒。上面结果中,有效期是20天(1728000秒),即允许缓存该条回应20天(1728000秒),在此期间,不用发出另一条预检请求。

    浏览器的正常请求和回应

    一旦浏览器通过了"预检"请求,以后每次浏览器正常的CORS请求,就都跟简单请求一样,会有一个origin头信息字段。服务器的回应,也都会有一个Access-Control-Allow-Origin头信息字段。

    服务端

    
        const Koa = require('koa'); 
       const route = require('koa-route');
       const app = new Koa();
       const fs = require('fs');
       const main = async ctx=>{
           ctx.set("Access-Control-Allow-Origin", "http://127.0.0.1:5500"); 这里是关键
           ctx.response.body = await fs.createReadStream('./1.json');
       }
       app.use(route.get('/',main))
       app.listen(3300)
    
    
  • 相关阅读:
    钉钉小程序解析html的方法
    git使用
    electron-vue项目
    数组方法大全
    关于AJAX
    常用的东西
    常用正则表达式
    选项卡
    进度条
    泛型单列
  • 原文地址:https://www.cnblogs.com/mengxiangji/p/10986726.html
Copyright © 2020-2023  润新知