4.8笔记跨域知识大全

涉及面试题:

  • 了解跨域么?
  • 跨域是由什么引起的呢?
  • 怎么解决跨域问题呢?
  • 浏览器为什么要阻止跨域请求?如何解决跨域?每次跨域请求都需要到达服务端吗?

面试知识点

什么是跨域

跨域问题其实就是浏览器的同源策略所导致的。

「同源策略」是一个重要的安全策略,它用于限制一个[origin]的文档或者它加载的脚本如何能与另一个源的资源进行交互。它能帮助阻隔恶意文档,减少可能被攻击的媒介。 --来源 MDN

当跨域的时候就会收到如下图这样的报错:

同源策略

什么是同源

「protocol(协议)、domain(域名)、port(端口)三者一致。」 一致的情况下我们叫同源。

同源示例

为什么浏览器不支持跨域

假设如今有a.com和b.com两个域,若是没有这一安全策略,那么当用户在访问a.com时,a.com的一段脚本就能够在不加载b.com的页面而随意修改或者获取b.com上面的内容。这样将会致使b.com页面的页面发生混乱,甚至信息被获取,包括服务器端发来的session。这样的话,web世界将是一片混乱。也是由于浏览器的同源策略,保证来至不一样源的对象不会互相干扰,保证了咱们访问页面最基本的安全。

9种解决方案

1、jsonp

JSONP是JSON with Padding的略称。它是一个非官方的协议,它允许在服务器端集成Script tags返回至客户端,通过javascript callback的形式实现跨域访问(这仅仅是JSONP简单的实现形式)。 由于同源策略限制,XMLHttpRequest只允许请求同源的资源,为了实现跨域请求,可以通过script标签来实现跨域请求,然后服务器输出json数据并执行回调函数,从而解决跨域请求。 href、src 都不受同源策略的限制。

原理

首先在客户端注册一个callback,然后把callback的名字传给服务器。此时,服务器先生成json数据,然后以JavaScript的语法方式,生成function,function的名字就是传递上来带参数的jsonp,最后将json数据直接以入参的方式,放置在function中,这样子就生成JavaScript语法文档,返回给客户端。客户端浏览器,通过解析,并执行返回JavaScript文档,此时数据作为参数,传入到客户端预先定义好的callback函数中,简单地说,就是利用script标签没有跨域限制地漏洞来达到第三方通讯的目的。

实现

	function jsonp({url,params,cb}){
		return new Promise((resolve, reject)=>{
			window[cb] = function(data){
				console.log(data)
				resolve(data);
				document.body.removeChild(script);
			}
			params= {...params,cb}
			let arrs = [];
			for (let key in params){
				arrs.push(`${key}=${params[key]}`)
			}
			let script = document.createElement('script');
			script.src = `${url}?${arrs.join('&')}`;
			script.onerror = () => reject('加载失败') 
			document.body.appendChild(script);
		

		})
	}
	jsonp({
		url:"http://localhost:3000/users",
		params:{name:"jin",age:12},
		cb:'show'
	}).then(data=>{
		console.log(data)
	})
复制代码

后端接口实现:

let express = require('express');
let app = express();
app.get('/users', function(req, res, next) {
	// 模拟的数据
	let {name,age,cb} = req.query
	let data = `"${name}现在${age}岁"`
	res.send(`${cb}(${data})`);
});

app.listen(3000)
复制代码

优缺点

1.优点

  • 它不像XMLHttpRequest对象实现的Ajax请求那样受到同源策略的限制,JSONP可以跨越同源策略;
  • 它的兼容性更好,在更加古老的浏览器中都可以运行,不需要XMLHttpRequest或ActiveX的支持
  • 在请求完毕后可以通过调用callback的方式回传结果。将回调方法的权限给了调用方。这个就相当于将controller层和view层终于分开了。我提供的jsonp服务只提供纯服务的数据,至于提供服务以 后的页面渲染和后续view操作都由调用者来自己定义就好了。如果有两个页面需要渲染同一份数据,你们只需要有不同的渲染逻辑就可以了,逻辑都可以使用同 一个jsonp服务。

2.缺点

  • 它只支持GET请求而不支持POST等其它类型的HTTP请求
  • 它只支持跨域HTTP请求这种情况,不能解决不同域的两个页面之间如何进行JavaScript调用的问题。
  • jsonp在调用失败的时候不会返回各种HTTP状态码。
  • 缺点是安全性。万一假如提供jsonp的服务存在页面注入漏洞,即它返回的javascript的内容被人控制的。那么结果是什么?所有调用这个 jsonp的网站都会存在漏洞。于是无法把危险控制在一个域名下,所以在使用jsonp的时候必须要保证使用的jsonp服务必须是安全可信的。

2、cors

CORS是一个W3C标准,全称是"跨域资源共享"

它允许浏览器向跨源服务器,发出XMLHttpRequest请求,从而克服了AJAX只能同源使用的限制。

浏览器将CORS请求分成两类:简单请求(simple request)和非简单请求(not-so-simple reque)。

属于简单请求条件,满足两大条件:

(1) 请求方法是以下三种方法之一:

  • HEAD
  • GET
  • POST

(2)HTTP的头信息不超出以下几种字段:

  • Accept
  • Accept-Language
  • Content-Language
  • Last-Event-ID
  • Content-Type:只限于三个值application/x-www-form-urlencodedmultipart/form-datatext/plain

对于简单请求,浏览器向服务器发起的request header中,会有一个origin字段,表示当前的“源”,服务器返回的response header中,会有一个Access-Control-Allow-Origin字段,里面写明允许那些“源”,浏览器发现两者一致,或者服务器允许所有的“源”,那么跨域成功!

简单请求实现

后端node-express 代码: 页面服务器挂3000端口,页面启动地址http://localhost:3000/index.html

let express = require('express');
let app = express();
app.use(express.static(__dirname));
app.listen(3000)
复制代码

请求发送4000端口实现跨域:

let express = require('express');
let app = express();
app.get('/getData', function(req, res, next) {
	console.log(req.headers)
	res.send("你拿不到数据了!");
});

app.listen(4000)
复制代码

正常发请发送到http://localhost:4000/getData

	var httpRequest = new XMLHttpRequest();//第一步:建立所需的对象
        httpRequest.open('GET', 'http://localhost:4000/getData', true);//第二步:打开连接  
        httpRequest.send();//第三步:发送请求  将请求参数写在URL中
        httpRequest.onreadystatechange = function () {
            if (httpRequest.readyState == 4) {
                if( httpRequest.status >= 200 &&  httpRequest.status <300 ||  httpRequest.status === 304 ){
                    var json = httpRequest.responseText;//获取到json字符串,还需解析
                    console.log(json);
                    }
            }
        };
复制代码

因为端口号不同就跨域了,如下图:

其实请求是发过去了,但是确实被浏览器屏蔽了结果.如果Origin指定的源,不在许可范围内,服务器会返回一个正常的HTTP回应。浏览器发现,这个回应的头信息没有包含Access-Control-Allow-Origin字段,就知道出错了,从而抛出一个错误,被XMLHttpRequestonerror回调函数捕获。注意,这种错误无法通过状态码识别,因为HTTP回应的状态码有可能是200。

如下图:

非简单请求实现

请求代码如下:

var httpRequest = new XMLHttpRequest();//第一步:建立所需的对象
document.cookie='name=jin'; //设置cookie
httpRequest.withCredentials = true;//允许请求携带cookie
httpRequest.open('POST', 'http://localhost:4000/getData', true);//第二步:打开连接 
httpRequest.setRequestHeader('name','Jack');
httpRequest.send();//第三步:发送请求  将请求参数写在URL中
httpRequest.onreadystatechange = function () {
      if (httpRequest.readyState == 4) {
          if( httpRequest.status >= 200 &&  httpRequest.status <300 ||  httpRequest.status === 304 ){
              var json = httpRequest.responseText;
              console.log(json);
          }
      }
};
复制代码

后端node代码:

let express = require('express');
let app = express();
app.all('*', function (req, res, next) {
  let origin = req.headers.origin
	//设置哪个源可以访问我
  res.header("Access-Control-Allow-Origin",origin);
	// 允许携带哪个头访问我
  res.header("Access-Control-Allow-Headers", "name");
	// 允许哪个方法访问我
  res.header("Access-Control-Allow-Methods", "POST");
	// 允许携带cookie
  res.set("Access-Control-Allow-Credentials", true);
	// 预检的存活时间
  res.header("Access-Control-Max-Age", 6);
	// 允许前端获取哪个头
	res.header("Access-Control-Expose-Headers", "name");
	// 请求头的格式
  res.header("Content-Type", "application/json;charset=utf-8");	
  next();
});
app.post('/getData', function(req, res, next) {
	console.log(req.headers)
	res.send("你拿不到数据了!");
});

app.listen(4000)
复制代码

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

非简单请求的CORS请求,会在正式通信之前,增加一次HTTP查询请求,称为"预检"请求(preflight)。

浏览器先询问服务器,当前网页所在的域名是否在服务器的许可名单之中,以及可以使用哪些HTTP动词和头信息字段。只有得到肯定答复,浏览器才会发出正式的XMLHttpRequest请求,否则就报错。 代码修改一下:

// 在请求头的设置中加上
if(req.method ==='OPTIONS'){
    res.end();//OPTIONS请求不做任何处理
}
复制代码

这时候,OPTIONS请求就不会再展示了。

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Proxy 代理对象使用详解即原理总结
 

我的理解就是:Proxy就是使用者和目标对象之间的一个中间商,使用者和对象之间的操作需要中间商代为完成,并且在完成使用者对对象的操作的前提下,中间商可以多加一点操作达到自己的目的。

重点
Proxy 只能代理对象。
代理指的是对一个对象基本语义的代理。读取和设置对象的属性都是基本语义,因为它们是一步就可以完成的操作。而调用一个对象内的函数就不是基本操作,因为它需要两步操作,第一步先拿到函数属性,第二步执行。
不能用 instanceof 判断代理对象的类型,因为 Proxy.prototype 是 undefined。
代理也可以代理另外一个代理形成多层拦截,如下例子:
let obj = {name:'孤城浪人'}
let proxy = new Proxy(obj,{
  get(target,property){
    console.log('第一层代理触发');
    return target[property];
  }
});
let proxy1 = new Proxy(proxy,{
  get(target,property){
    console.log('第二层代理触发');
    return target[property];
  }
});
console.log(proxy1.name);

有很多其他代理工具和依赖,这边介绍最常用的:

1)Webpack (4.x)

webpack中可以配置proxy来快速获得接口代理的能力。

const path = require("path");
const HtmlWebpackPlugin = require("html-webpack-plugin");

module.exports = {
  entry: {
    index: "./index.js"
  },
  output: {
    filename: "bundle.js",
    path: path.resolve(__dirname, "dist")
  },
  devServer: {
    port: 8000,
    proxy: {
      "/api": {
        target: "http://localhost:8080"
      }
    }
  },
  plugins: [
    new HtmlWebpackPlugin({
      filename: "index.html",
      template: "webpack.html"
    })
  ]
};
复制代码

2) Vue-cli 2.x  

// config/index.js

...
proxyTable: {
  '/api': {
     target: 'http://localhost:8080',
  }
},
...
复制代码

3) Vue-cli 3.x

// vue.config.js 如果没有就新建
module.exports = {
  devServer: {
    port: 8000,
    proxy: {
      "/api": {
        target: "http://localhost:8080"
      }
    }
  }
};

————————————————
 

8、nginx

Nginx 是一个高性能的HTTP和反向代理web服务器,同时也提供了IMAP/POP3/SMTP服务。Nginx 采用的是多进程(单线程)和多路IO复用模型

nginx应用场景

  • 静态资源服务器
  • 反向代理服务
  • API接口服务(Lua&Javascript)

nginx优势

  • 高并发高性能
  • 可扩展性好
  • 高可靠性
  • 热布署
  • 开源许可证

nginx 安装

nginx.conf配置文件实现跨域

location 匹配路径遇到json结尾的访问 /data/json的目录文件, 设置add_header\color{#FF0000}{ add\_header}add_header 配置允许请求跨域

server { # 每个server对应一个网站
        listen       80; # 监听的端口号
        server_name  localhost; #域名
        access_log off;

        # 有些指令可以支持正则表达式
        location / { #匹配所有的路径
            root   /usr/share/nginx/html; #静态文件根目录
            index  index.html index.htm; #索引文档

        }
      location ~ .*\.json$ { 
        add_header Access-Control-Allow-Origin http://localhost:3000;
        add_header Access-Control-Allow-Methods GET,POST,PUT,DELETE,OPTIONS;
        root /data/json;
}
    }
复制代码

nginx反向代理都可以实现跨域。代理的思路为,利用服务端请求不会跨域的特性,让接口和当前站点同域。

location ~ ^/api {
    proxy_pass http://localhost:3000;
    proxy_redirect default; #重定向

    proxy_set_header Host $http_host;        #向后传递头信息
    proxy_set_header X-Real-IP $remote_addr; #把真实IP传给应用服务器

    proxy_connect_timeout 30; #默认超时时间
    proxy_send_timeout 60;    # 发送超时
    proxy_read_timeout 60;    # 读取超时


    proxy_buffering on;             # 在proxy_buffering 开启的情况下,Nginx将会尽可能的读取所有的upstream端传输的数据到buffer,直到proxy_buffers设置的所有buffer们 被写满或者数据被读取完(EOF)
    proxy_buffers 4 128k;           # proxy_buffers由缓冲区数量和缓冲区大小组成的。总的大小为number*size
    proxy_busy_buffers_size 256k;   # proxy_busy_buffers_size不是独立的空间,他是proxy_buffers和proxy_buffer_size的一部分。nginx会在没有完全读完后端响应的时候就开始向客户端传送数据,所以它会划出一部分缓冲区来专门向客户端传送数据(这部分的大小是由proxy_busy_buffers_size来控制的,建议为proxy_buffers中单个缓冲区大小的2倍),然后它继续从后端取数据,缓冲区满了之后就写到磁盘的临时文件中。
    proxy_buffer_size 32k;          # 用来存储upstream端response的header
    proxy_max_temp_file_size 256k; # response的内容很大的 话,Nginx会接收并把他们写入到temp_file里去,大小由proxy_max_temp_file_size控制。如果busy的buffer 传输完了会从temp_file里面接着读数据,直到传输完毕。
}
复制代码

9、websocket

WebSocket 是一种网络通信协议,很多高级功能都需要它。

特点:服务器可以主动向客户端推送信息,客户端也可以主动向服务器发送信息,是真正的双向平等对话,属于服务器推送技术的一种。

其他特点包括:

(1)建立在 TCP 协议之上,服务器端的实现比较容易。

(2)与 HTTP 协议有着良好的兼容性。默认端口也是80和443,并且握手阶段采用 HTTP 协议,因此握手时不容易屏蔽,能通过各种 HTTP 代理服务器。

(3)数据格式比较轻量,性能开销小,通信高效。

(4)可以发送文本,也可以发送二进制数据。

(5)没有同源限制,客户端可以与任意服务器通信。

(6)协议标识符是ws(如果加密,则为wss),服务器网址就是 URL。

实现

html

	//
		let socket = new WebSocket('ws://localhost:3000');
		socket.onopen = function(){
			socket.send('I love you')
		}
		socket.onmessage = function (e){
			console.log(e.data)
		}
复制代码

服务器使用node,用ws的依赖

let express = require('express');
let app = express();
let WebSocket = require('ws');
let wss = new WebSocket.Server({port:3000})
wss.on('connection',function(ws){
	ws.on('message',function(data){
		console.log(data)
		wx.send('server data');//服务器发送消息
	})
})

app.listen(4000)
复制代码

这部分有兴趣的朋友可以看阮一峰老师的WebSocket 

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值