前端常见浏览器跨域请求解决方案

在浏览器请求中,出现跨域访问资源的问题,我们肯定会遇到。如果跨域请求被阻止,有可能导致css、js 、ajax请求、font字体等资源出现无法正常访问的问题。


什么是跨域?

跨域是指一个域下的文档或者脚本试图去请求另一个域下的资源。

也就是说:当前发起请求的域和该请求指向的资源所在的域不一致。
具体表现在 协议域名端口号——均一致则为同域,有一个不同就是跨域。

广义的跨域
  1. 资源跳转:a链接跳转、重定向、表单提交;
  2. 资源嵌入:<link><script><frame>等DOM操作标签;
  3. js 发起的 ajax 请求、dom 和 js 对象的跨域操作等;
狭义的跨域

浏览器同源策略限制

同源策略

同源策略/SOP(Same origin policy)是一种约定,由Netscape公司1995年引入浏览器,它是浏览器最核心也最基本的安全功能,如果缺少了同源策略,浏览器很容易受到XSS、CSFR等攻击。所谓同源是指"协议+域名+端口"三者相同,即便两个不同的域名指向同一个ip地址,也非同源。

同源策略限制:
  1. CookieLocalStorageIndexDB 无法读取
  2. DOMJS 对象无法获取
  3. AJAX 请求不能发送
注意:
  1. 如果是协议和端口造成的跨域问题“前台”是无能为力的;
  2. 在跨域问题上,域仅仅是通过“URL的首部”来识别而不会去尝试判断相同的ip地址对应着两个域或两个域是否在同一个ip上。

为什么要跨域?

  1. 在前端开发过程中,经常会遇到需要调用第三方服务接口的情况;
  2. 现在的项目大多采用前后端分离的模式,前端后端的域名不一致,因此需要解决跨域问题。

前端跨域解决方案?

一、jsonp跨域

  • 通常为了减轻web服务器的负载,我们把js、css,img等静态资源分离到另一台独立域名的服务器上,在html页面中再通过相应的标签从不同域名下加载静态资源,而被浏览器允许,基于此原理,我们可以通过动态创建script,再请求一个带参网址实现跨域通信。
  • 简单点来说就是:动态创建script,再请求一个带参网址实现跨域通信(script 标签的 src 属性不受同源策略的限制 );
  • 缺点是只能实现 get 一种请求,容易遭受 xss 攻击。

二、CORS(跨域资源共享)

普通跨域请求:只服务端设置Access-Control-Allow-Origin即可,前端无须设置,若要带cookie请求:前后端都需要设置。
需注意的是:由于同源策略的限制,所读取的cookie为跨域请求接口所在域的cookie,而非当前页。
目前,所有浏览器都支持该功能(IE8+:IE8/9需要使用XDomainRequest对象来支持CORS)),CORS也已经成为主流的跨域解决方案。

# 配置 cors 跨域
header("Access-Control-Allow-Origin:*");
header("Access-Control-Request-Methods:GET, POST, PUT, DELETE, OPTIONS");
header('Access-Control-Allow-Headers:x-requested-with,content-type,test-token,test-sessid');

三、nginx配置代理(proxy)

配置代理分为正向代理和反向代理两种形式。

正向代理
  • 有一个客户端需要向一个非同源的服务器B发送请求,我们搭建一个和客户端同源的服务器A,当客户端发送请求的时候,由服务器A来接受,再由服务器A向服务器B发送请求,因为 同源策略是由浏览器给的,服务器之间没有。
  • 服务器B接受到请求以后,会处理请求,并把响应返回给服务器A,再由服务器A把响应给到客户端就可以了。我们就可以用这个方式来进行跨域请求了。
反向代理
  • 反向代理一般是用来做负载均衡
  • 当我请求一个服务器的时候,其实请求的是服务器端设置的代理服务器,由代理服务器把若干大量的请求分发给不同的服务器进行处理,再由服务器把响应给到代理服务器,代理服务器返回给客户端。
服务器配置文件
  • ngnix 后台配置
server {
	   # 监听的端口号
	   listen        80;
	   # 服务器名称;
	   server_name  localhost;
	   # 根目录;
	   root   "D:/phpstudy_pro/WWW";
	
	   # 配置的代理;(在这里配置)
	   location = /baidu {
	       proxy_pass https://www.baidu.com/sugrec;
	   }
	   location = /pxx {
	       proxy_pass https://apiv2.pinduoduo.com/api/gindex/subject/limited/goods;
	   }
	
	   # location => 地址栏信息;
	   # http://localhost/php/day26/xxx.html;
	   # http://   : 给浏览器看的 , 让浏览器知道用什么样的方式去进行请求的发送,响应的接受;
	   # localhost : 给浏览器看的,让浏览器可以向对应的ip地址(目标)发起请求;
	   # /php/day26/xxx.html 服务器查询语句;
	   # 在nginx之中由 location 来进行捕获并处理;
	   # http://localhost : 默认就是传递一个 / 给服务器;
	   # file:///D:/phpstudy_pro/WWW/index.html
	
	   # 重要 : 如果更改了配置文件,那么必须重启服务器;
	   location / {
	       index index.php index.html;
	       error_page 400 /error/400.html;
	       error_page 403 /error/403.html;
	       error_page 404 /error/404.html;
	       error_page 500 /error/500.html;
	       error_page 501 /error/501.html;
	       error_page 502 /error/502.html;
	       error_page 503 /error/503.html;
	       error_page 504 /error/504.html;
	       error_page 505 /error/505.html;
	       error_page 506 /error/506.html;
	       error_page 507 /error/507.html;
	       error_page 509 /error/509.html;
	       error_page 510 /error/510.html;
	       autoindex  on;
	   }
	
	   # .php后缀的内容需要用php解析;
	   # /\.php(.*)$/ 正则
	   location ~ \.php(.*)$ {
	       fastcgi_pass   127.0.0.1:9000;
	       fastcgi_index  index.php;
	       fastcgi_split_path_info  ^((?U).+\.php)(/?.+)$;
	       fastcgi_param  SCRIPT_FILENAME  $document_root$fastcgi_script_name;
	       fastcgi_param  PATH_INFO  $fastcgi_path_info;
	       fastcgi_param  PATH_TRANSLATED  $document_root$fastcgi_path_info;
	       include        fastcgi_params;
	   }   
}
  • 前端 js 配置
<script>
   // 1. 找到nginx服务器配置文件;
    // 2. 进行代理配置;
    var options = {
        url : "http://localhost/baidu",
        success : function(res){
            console.log(res);
        },
        dataType : "json",
        data : {
            wd : "你好",
            prod : "pc",
            sugsid : "1423,31169,21125,30839,31187,30823,22159",
        }
    }
    ajax(options)
</script>

四、Nodejs中间件代理

node中间件实现跨域代理,原理大致与nginx相同,都是通过启一个代理服务器,实现数据的转发
使用 node + express + http-proxy-middleware 搭建一个proxy服务器。

  • 前端代码示例
	var xhr = new XMLHttpRequest();
	
	// 前端开关,浏览器是否读写cookie
	xhr.withCredentials = true;
	
	// 访问http-proxy-middLeware代理服务爨
	xhr.open( 'get', 'http://www.domain1.com:3000/login?user=admin', true);
	xhr.send();
  • 中间件服务器代码示例
	var express = require( 'express' );
	var proxy = require( 'http-proxy-middleware');
	var app = express();
	
	app.use("/" , proxy({
		// 代理跨域目标接口
		target: 'http://www.domain2.com:808e',
		changeorigin: true,
		// 修改响应头信息,实现跨域并允许带cookie
		onProxyRes: function(proxyRes, req, res) {
			res.header( 'Access-Control-Allow-Origin', 'http://www.domain1.com');
			res.header( 'Access-Control-Allow-Credentials ', 'true' );
		},
		// 修改响应信息中的cookie域名
		cookieDomainRewrite: 'www.domain1.com' //可以为false,表示不修改
	}));
	app.listen( 3000);
	console.log('Proxy server is listen at port 3000...');
  • nodejs 后台代码示例同 三、nginx配置代理(proxy)

五、Vue 跨域

  1. 这里以使用 vue-cli 脚手架生成的标准项目为准。一般在项目根目录下有 vue.config.js 文件

    module.exports = {
        publicPath: './',
        lintOnSave: true,
        devServer: {
            proxy: {
                '/api': {
                    target: 'http://192.168.2.90:3000',	  //后端接口地址
                    changeOrigin: true,		//是否允许跨越
                    pathRewrite: {
                        '^/api': '/api'  	//重定向,
                    }
                }
            }
        }
    }
    

    此方法只能在开发环境中使用。
    后端请求地址是http://192.168.2.90:3000,所有api的接口 url 都以 /api 开头。
    所以首先需要匹配所有以 /api 开头的.然后修改 target 的地址为http://192.168.2.90:3000
    最后修改 pathRewrite 地址:将前缀 '^api' 转为 '/api'
    如果本身的接口地址就有'/api'这种通用前缀,就可以把pathRewrite 删掉。

  2. 配置一下 axios.defaults.baseURL = '/api’ 这样就可以保证动态的匹配生产和开发环境的定义前缀,代码如下:

    // mock服务器
    axios.defaults.baseURL ='/api';
    
  3. 重新启动项目,解决跨域问题

六、WebSocket协议

web sockets是一种浏览器的API,它的目标是在一个单独的持久连接上提供全双工、双向通信。(同源策略对web sockets不适用)

web sockets原理:在JS创建了web socket之后,会有一个HTTP请求发送到浏览器以发起连接。取得服务器响应后,建立的连接会使用HTTP升级从HTTP协议交换为web sockt协议。

只有在支持web socket协议的服务器上才能正常工作。

var socket = new WebSockt('ws://www.baidu.com'); //http->ws; https->wss
socket.send('hello WebSockt');
socket.onmessage = function(event){
    var data = event.data;
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值