阅读 79

跨域,同源,JSONP,CORS记录

1.同源策略于跨域

1.1 同源策略

首先我认为想要了解跨域前必须先要知道浏览器的同源策略。

同源策略(Same origin policy)是一种约定,它是浏览器最核心也最基本的安全功能,如果缺少了同源策略,则浏览器的正常功能可能都会受到影响。可以说Web是构建在同源策略基础之上的,浏览器只是针对同源策略的一种实现。

同源指的是协议、域名、端口相同。

同源策略是客户端脚本(尤其是Javascript)的重要的安全度量标准,其目的是防止某个文档或脚本从多个不同 源装载。

其实在13年之前,在还没有提出前后端分离时。大多项目都是同源的,因为项目的前端页面不是复杂,通常直接将前端页面放在服务器上,直接进行服务器渲染。用户直接请求到html页面。

但是随着时间的推移前端界面变得越来越重要。如果将所以的前端界面放在服务器进行服务器端渲染,这无疑增加了服务器的压力。这时候就提出了前后端分离。所谓前后端分离就是前端有自己的web服务器,后端只提供数据,web服务器在另一边进行请求。这样就产生了一个问题。跨域

1.2 跨域

经过上面的描述,或许你已经基本清楚了跨域时怎么回事。

首先跨域是浏览器为了防止出现安全问题的一种机制

我们来看一下跨域的具体表现:

  1. 协议不同,如http, https
  2. 端口不同
  3. 主域相同,子域不同;
  4. 主域不同;
  5. IP地址和域名之间也算是跨域,浏览器不会自动做IP域名的映射

来看一个典型的跨域的例子: 现在我的服务器端口号是8000,web服务器的端口是5050。在web页面直接进行Ajax请求。会出现以下的错误内容:

在这里插入图片描述

读一下报错信息大概的意思就是http请求无法将端口为8000的服务器的数据读到5500端口的web界面。

1.3 为什么要设定跨域这个概念

上面也说到跨域是浏览器设定的一种机制。

细心的同学可能已经出现跨域情况时浏览器给我们的状态还是200。这就足以说明跨域是浏览器的机制,只是数据被拦截。请求时成功的。

在这里插入图片描述

那么到底到底为什么会有跨域呢?看一下几个场景:

1.用户登录了自己的aaa页面 aaa.com,http://aaa.com向用户…

2.用户浏览了恶意页面 bbb.com。执行了页面中的恶意AJAX请求代码。

3.bbb.com向http://aaa.com发起A… HTTP请求,请求会默认把aaa.com对应cookie也同时发送 过去。

4.aaa页面从发送的cookie中提取用户标识,验证用户无误,response中返回请求数据。此时数据就泄露了。

5.而且由于Ajax在后台执行,用户无法感知这一过程。

2.解决跨域的方案

其实解决跨域的方式一共有7种,我这里一一列举出来

  1. jsonp
  2. cors
  3. 基于proxy(webpack-dev-server实现)
  4. postmessage
  5. socket.io
  6. iframe解决
  7. nginx反向代理

我们前端经常使用的其实前三种,jsonp其实相对于后两者有劣势。在实际开发种最最常用的还是cors和proxy。

但是本文介绍以下前两种(面试点)

2.1 jsonp

jsonp的原理其实有一点套娃的意思。使用回调函数"骗过"浏览器。

我们可以通过使用html的script标记来进行跨域请求,并在相应中返回要执行的script代码,其中可以直接使用JSON传递javascript对象。这种跨域的通讯方式成为JSONP。

因为script的src属性本身就是一个不会产生跨域的get请求。所以我们可以将回调函数写在URL中,然后再服务端处理传值,再再客户端设置一个全局的函数来执行回调函数。

下面我们来实现一个简单的jsonp案例:

客户端server.js:

const http = require('http');
const urly = require('url');
var obj={
    name:'jam',
    age:'12'
}
http.createServer((req,res) => {
    var parmer=urly.parse(req.url,true)

    console.log(parmer);
    //判断是否跨域(是否有parmer.query.callback这个值,如果有,拼接成带有参数的函数,传给客户端)
    if (parmer.query.callback) {
        var str=parmer.query.callback+'('+JSON.stringify(obj)+')'
        res.write(str);
    }else{
        res.write(JSON.stringify(obj));
    }
    res.end();
}).listen(8000,function () { 
    console.log('服务器开启');
 });
复制代码

客户端client.js:

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<script>
    //要执行的回调韩素华
    function hello(data) {
        console.log(data);
    }
</script>
  //使用script标签进行get请求,后接要传给服务端的回调函数
<script src="http://127.0.0.1:8000/?callback=hello"></script>
<body>
</body>
</html>
复制代码

我们经常使用的jQuery中进行Ajax请求时设置的dataType属性为jsonp其实也是使用的这个原理。

不足之处:

  1. 只能进行get请求,标签限定
  2. 需要定义全局函数

2.2 CORS

跨域资源共享

它允许浏览器向跨源服务器,发出XMLHttpRequest请求,从而克服了AJAX只能同源使用的限制。 CORS需要浏览器和服务器同时支持。目前,所有浏览器都支持该功能,IE浏览器不能低于IE10。 整个CORS通信过程,都是浏览器自动完成,不需要用户参与。对于开发者来说,CORS通信与同源的AJAX通信 没有差别,代码完全一样。浏览器一旦发现AJAX请求跨源,就会自动添加一些附加的头信息,有时还会多出一次附 加的请求,但用户不会有感觉。 因此,实现CORS通信的关键是服务器。只要服务器实现了CORS接口,就可以跨源通信。

这种方式也是我们经常使用的,通常在后端进行设定CORS相关属性。前端直接请求即可。

下面我贴出在express框架中的使用方法。

手动:

app.use((req, res, next) => {
res.header('Access-Control-Allow-Origin', '*')
res.header('Access-Control-Allow-Headers', 'Authorization,X-API-KEY, Origin, X-Requested-With, Content-Type, Accept, Access-Control-Request-Method' )
res.header('Access-Control-Allow-Methods', 'GET, POST, OPTIONS, PATCH, PUT, DELETE')
res.header('Allow', 'GET, POST, PATCH, OPTIONS, PUT, DELETE')
next();
});
复制代码

使用cors包(封装):

npm install cors --save-dev

const cors = require('cors');
app.use(cors());
复制代码

我们这里需要注意的是在设置Origin字段时可以有两种情况:*和一个域名

在使用*(接受所有域名)的时候浏览器为了安全考虑是不会发送Cookie值的,使用一个域名的时候是可以的。但是在实际开发中我们通常只需要使用一个域名就可以完成业务了。

2.3 CORS和jsonp的比较

  1. jsonp可以支持老式浏览器,但是处理get请求。
  2. CORS支持几乎所有方式,当时不支持IE10一下浏览器
  3. 综合下来CORS是要比jsonp强大的。

好了关于后面的方式proxy也是比较常见的,剩下的就用的不太多了。

关于proxy后面再去整理

文章分类
前端
文章标签