详解跨域处理

非同源策略(跨域)请求的处理方案

1. 聊聊你对跨域的理解

前端开发进化史

  • 服务器渲染 「半服务器渲染 SSR」

  • 客户端渲染

详解跨域处理_第1张图片

为啥会产生跨域

  • 服务器分离:WEB服务器、数据服务器、图片服务器…

  • 云信息共享:第三方API接口

  • 有助于分离开发:开发跨域、部署同源

  • ……

解决方案

  • JSONP

  • CORS

  • Proxy

  • ……

 同源策略请求 && 非同源策略(跨域)请求

   HTML页面预览的地址:http://127.0.0.1:5500/index.html

   基于Ajax发送请求的数据接口地址:http://127.0.0.1:9999/user/list

   如果两个地址的“协议、域名、端口号”完全一致,则是同源请求,只要有一个不一样,则就是跨域请求!!

      + 同源请求:把web页面和后台的接口程序部署在“相同服务器的相同服务下”

      + 跨域请求:页面和后台分开部署「同一台服务器的不同端口下、或者不同服务器下...」

   项目场景:

      + 开发的时候:我们在自己的电脑上启动服务预览项目,但是后台的程序在其他的服务器上,没有在一起,此时的数据请求就是“跨域”的!!

      + 部署到服务器:

        + 全部署在一起(相同服务器的相同服务下),此时就是同源「不建议」

        + 当代部署一般都是分服务器部署,也就是页面和后台是部署在不同服务器上的,此时就是跨域

      + ...

   默认情况下不允许Ajax跨域请求,因为浏览器有“安全策略”

      Access to XMLHttpRequest at 'https://www.jianshu.com/asimov...' from origin 'http://127.0.0.1:5500' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.

   服务器和服务器之间默认是没有跨域限制的

 ==============================

 非同源策略(跨域)请求方案:

   + proxy跨域代理:原理就是利用“服务器和服务器之间不存在跨域”来实现的

     步骤:

       @1 在本地启动一个服务:一方面可以实现页面的预览,另一方面也可以代理转发数据请求

       @2 我们以后再发数据请求,发送给本地启动的这个服务,而这个服务会去真正的服务器上获取到相关的数据,再返回给客户端!!

     + 本地开发的时候,我们基于proxy跨域代理来完成「webpack-dev-server、Node...」

     + 部署到服务器的时候,我们一般是基于nginx的反向代理来处理

   + CORS跨域资源共享:默认因为浏览器的安全策略,是不允许ajax跨域访问的,但是如果服务器设置了 Access-Control-Allow-Origin 响应头信息,设置允许这个源发送请求,那么浏览器也就不在去限制了!!

     步骤:

       @1 由服务器设置允许源

         Access-Control-Allow-Origin 设置允许的源「白名单机制」

         Access-Control-Allow-Credentials 是否允许携带资源凭证「例如:cookie」

           服务器端设置了允许,则客户端也要配合设置允许

           axios.defaults.withCredentials = true;

           xhr.withCredentials=true;

         Access-Control-Allow-Headers 设置允许的请求头

         Access-Control-Allow-Methods 设置允许的请求方式

       @2 在CORS跨域共享中,客户端首先会尝试发送一个OPTIONS试探请求,验证是否连接成功,连接成功后,再发送真正的请求!!

   + JSONP跨域解决方案

     原理:利用了

 服务器端:

/*-CREATE SERVER-*/
const express = require('express'),
    app = express();
app.listen(1001, () => console.log(`服务启动成功,正在监听1001端口!`));

app.get('/user/list', (req, res) => {
    // 获取传递进来的callback值,例如:'func'
    let { callback } = req.query;
    // 准备数据
    let result = {
        code: 0,
        data: ['张三', '李四']
    };
    // 返回给客户端指定的格式,例如:’函数名(数据)‘
    res.send(`${callback}(${JSON.stringify(result)})`);
});

/* STATIC WEB */
app.use(express.static('./'));

proxy 

/*-CREATE SERVER-*/
const express = require('express'),
    request = require('request'),
    app = express();
app.listen(1001, () => console.log(`服务启动成功,正在监听1001端口!`));

// 服务器接收到客户端发送过来的请求
app.get('/aaa', (req, res) => {
    // 向简书发送相同请求,从简书服务器获取想要的数据「不存在域的限制的」
    let jianURL = `https://www.jianshu.com/asimov/subscriptions/recommended_collections`;
    req.pipe(request(jianURL)).pipe(res);
});

/* STATIC WEB */
app.use(express.static('./'));

CORS

/*-CREATE SERVER-*/
const express = require('express'),
	app = express();
app.listen(1001, () => console.log(`服务启动成功,正在监听1001端口!`));

/*-MIDDLE WARE-*/
// 设置白名单
let safeList = ["http://127.0.0.1:5500", "http://127.0.0.1:3000", "http://127.0.0.1:8080", "http://127.0.0.1:5501"];
app.use((req, res, next) => {
	let origin = req.headers.origin || req.headers.referer || "";
	origin = origin.replace(/\/$/g, '');
	origin = !safeList.includes(origin) ? '' : origin;
	res.header("Access-Control-Allow-Origin", origin);
	res.header("Access-Control-Allow-Credentials", true);
	res.header("Access-Control-Allow-Headers", "Content-Type,Content-Length,Authorization, Accept,X-Requested-With");
	res.header("Access-Control-Allow-Methods", "PUT,POST,GET,DELETE,OPTIONS,HEAD");
	req.method === 'OPTIONS' ? res.send('OK') : next();
});

/*-API-*/
app.get('/list', (_, res) => {
	res.send({
		code: 0,
		message: 'zhufeng'
	});
});

/* STATIC WEB */
app.use(express.static('./'));

vue.config.js

webpack-dev-server

const ENV = process.env.NODE_ENV;
module.exports = {
    lintOnSave: ENV !== 'production',
    publicPath: './',
    productionSourceMap: false,
    // 对webpack-dev-server的配置
    devServer: {
        // 配置跨域代理「可以配置对多台服务器的代理」
        proxy: {
            // 所有以“/api”开始的请求,都发送到代理服务器「走这个配置」
            "/api": {
                target: "https://www.jianshu.com/asimov",
                ws: true,
                changeOrigin: true,
                pathRewrite: {
                    "^/api": ""
                }
            },
            // 所有以“/zhihu”开始的请求,都代理到知乎服务器
            "/zhihu": {
                target: "https://news-at.zhihu.com/api/4",
                ws: true,
                changeOrigin: true,
                pathRewrite: {
                    "^/zhihu": ""
                }
            }
        }
    }
};

部署到服务器上:基于nginx实现反向代理

详解跨域处理_第3张图片

处理原理

自己基于node实现


const request = require('request');

app.get('/subscriptions/recommended_collections', function (req, res) {

let url = 'https://www.jianshu.com/asimov' + req.url;

req.pipe(request(url)).pipe(res);

});


扩展:其他跨域方案「配合iframe」

  • postMessage

  • window.name

  • document.domin

  • location.hash

  • ……

----------------------------------------

你可能感兴趣的:(JS高级,笔记,服务器,servlet,ajax)