前端常用的跨域方案

93 阅读1分钟

前端常用的跨域方案

目录

[TOC]

前言

发送请求XMLHttpRequest(ajax, $ajax, axois),fetch

http://192.168.0.103:8080/

file:///F://vue阶段/vue工程化/_systerm/public/index.html:file协议不允许发送ajax请求

平时项目中,同源请求需求很少,服务器部署的时候是分开的(web服务器,数据服务器),调用第三方平台接口,本地开发的时候,在自己电脑上预览项目,而数据接口是请求其他服务器的

开发的时候跨域,项目部署的时候,是部署到同一台服务器上的同一个服务下(生产同源)

前后端不分离的项目,开发的时候本地也会把后台服务启动,所以开发也是同源,部署也是同源

全栈开发,基于SSR渲染(js+nodejs)这也是同源的

跨域

浏览器有安全策略限制,默认情况下是不允跨域请求的(web页面地址vs请求接口地址 协议,域名,端口号,三者有一个不一致就是跨域)

解决跨域方案

一.修改本地HOST:

开发的时候是跨域的,但是部署的时候是同源的,我们只要解决开发跨域问题即可

  • DNS解析-->找本地的DNS缓存记录(本地host文件中查找)
  • 客户端浏览器地址栏输入www.baidu.com
  • host配置:www.baidu.com:80  127.0.0.1:80

这样保证饿了浏览器是www.baidu.com但是访问的是本地开发的这个项目,在这个基础上我们去www.baidu.com/user/list发请求,相当于欺骗了浏览器,让浏览器认为我是同源的

二.JSONP

script标签的src请求资源文件(基于GET请求方式)他是不存在跨域限制

JSONP的原理就是利用了这个机制,

只能是GET请求,服务器必须服务器支持

创建一个全局函数 把函数通过问号传参方式发送给服务器

代码

  <script>
        (function () {
            window['fn'] = function fn(result) {
                console.log(result);
            };
        })();
    </script>
    <script src="https://www.baidu.com/sugrec?prod=pc&wd=周冬雨&cb=fn"></script>

封装jsonp

基于promise管理封装jsonp

  • 1.初始化参数,不传 默认:params=null, 函数名为callback
  • 2.创建全局函数
  • 3.处理url:把参数函数名,基于问号参数形式,拼接到url末尾
  • 4.发送请求:创建script标签插入到页面中
(function () {
    // 检测是否为纯粹对象
    const isPlainObject = function isPlainObject(obj) {
        let proto, Ctor;
        if (!obj || Object.prototype.toString.call(obj) !== "[object Object]") return false;
        proto = Object.getPrototypeOf(obj);
        if (!proto) return true;
        Ctor = proto.hasOwnProperty('constructor') && proto.constructor;
        return typeof Ctor === "function" && Ctor === Object;
    };
 
    // 把普通对象变为URLENCODED格式字符串
    const stringify = function stringify(obj) {
        let str = ``,
            keys = Object.keys(obj).concat(Object.getOwnPropertySymbols(obj));
        keys.forEach(key => {
            str += `&${key}=${obj[key]}`;
        });
        return str.substring(1);
    };
 
    /* 封装JSONP函数 */
    const jsonp = function jsonp(url, config) {
        return new Promise((resolve, reject) => {
            // 初始化参数
            if (typeof url !== "string") throw new TypeError('url is not a string!');
            if (!isPlainObject(config)) config = {};
            config = Object.assign({
                params: null,
                jsonp: 'callback'
            }, config);
 
            // 创建一个全局函数
            let f_name = `jsonp${+new Date()}`;
            window[f_name] = value => {
                // 请求成功
                resolve(value);
                delete window[f_name];
                document.body.removeChild(script);
            };
 
            // 处理URL「拼接问号参数 & 拼接函数名」
            let params = config.params;
            if (params) {
                if (isPlainObject(params)) params = stringify(params);
                url += `${url.includes('?')?'&':'?'}${params}`;
            }
            //拼接函数
            url += `${url.includes('?')?'&':'?'}${config.jsonp}=${f_name}`;
 
            // 发送请求
            let script = document.createElement('script');
            script.src = url;
            script.onerror = err => {
                // 请求失败
                reject(err);
            };
            document.body.appendChild(script);
        });
    };
 
    /* 暴露API */
    if (typeof module === "object" && typeof module.exports === "object") module.exports = jsonp;
    if (typeof window !== "undefined") window.jsonp = jsonp;
})();

调用

    <script>
        jsonp('https://www.baidu.com/sugrec', {
            //问号传参值&&关键词
            params: {
                prod: 'pc',
                wd: '周冬雨'
            },
            jsonp: 'cb'//默认传递的函数名callback
        }).then(value => {
            console.log(value);
        });
    </script>
三.CORS

CORS跨域资源共享:只要服务器端设置允许源即可,允许客户端发送请求,这样就可以忽略浏览器的安全策略,  ,客户端不用做什么,最多配置一个允许携带资源凭证    withCredentials: true

  • 服务器设置Access-Control-Allow-Origin",
  • 设置为*:允许所有原访问(不安全),不允许携带资源凭证(例如cooike)Access-Control-Allow-Credentials", 必须为false
  • 不设置*号,只能设置单一源,但是可以携带资源凭证
  • 客户端向服务器发请求一般都有orgin 和referer 记录客户端的 协议,域名,端口号,服务器根据字段设置白名单
四 proxy

proxy跨域代理:利用后端和后端的通信默认没有安全策略限制的

中间代理服务器,预览web页面,帮助我们从其他服务器获取资源

开发环境node.js自己写, vue react==>webpack-dev-server

生成环境下nginx反向代理

代码

  devServer: {
        proxy: {
            '/': {
                target: 'http://127.0.0.1:9999',
                changeOrigin: true
            }
        }
    }