JS基础——之前端跨域请求解决方案

跨域问题解决

浏览器遵循的同源策略主要是指协议、域名、端口一致

只要任意一个不一样,就不是同源,触发了同源策略的请求我们叫跨域请求

基于http协议的跨域解决办法:

一、jsonp解决跨域请求

原理:script标签可以执行js代码,会把src指定的文件或地址里面的内容当作js代码执行,src属性不受同源策略的影响,这样我们可以在src属性中放置一个非同源的服务器,服务器可以返回一个字符串,前端拿到这个字符串当作js代码执行。

过程:

​ 1.我们可以在src属性中放置一个非同源的服务器地址,在地址后面携带一个带有函数名的参数

​ 2.服务器可以读取在这个函数名后面的参数,通过函数名(数据)这种字符串形式进行返回。

​ 3.前端拿到这个字符串当作js代码执行。

缺点:

只能实现get一种请求。

1)原生js代码实现:

<script>
  var script = document.createElement('script');
	script.type = 'text/javascript';
	// 传参一个回调函数名给后端,方便后端返回时执行这个在前端定义的回调函数
	script.src = 'http://www.domain.com:8080/login?user=admain&callback=handleCallback';
	document.head.appendChild(script);
	// 回调执行函数
	function handleCallback(res) {
    alert(JSON.stringtify(res));
  }
</script>

2)jquery ajax实现:

$.ajax({
  url: 'http://www.domain.com:8080/login',
  type: 'get',
  dataType: 'jsonp', // 请求方式为jsonp
  jsonpCallback: 'handleCallback', // 自定义回调函数名
  data: {}
})
  1. vue.js实现
this.$http.jsonp('http://www.domain.com:8080/login', {
  params: {},
  jsonp: 'handleCallback'
}).then((res)=> {
  console.log(res);
})
二、代理解决跨域请求(nodejs中间件代理跨域)

代理——利用正向代理实现

apache代理http协议免费 代理https协议收费

nginx代理http和https协议均免费

1. vue框架的跨域(1次跨域)

利用node+webpack+webpack-dev-server代理接口跨域。在开发环境下,由于vue渲染服务器和接口代理服务都是webpack-dev-server同一个,所以页面与代理接口之间不再跨域,无须设置headers跨域信息了

webpack.config.js部分配置:

module.exports = {
  entry: {},
  module: {},
  ...
  devServer: {
    historyApiFallback: true,
    proxy: [{
      context: '/login',
      target: 'http://www.domain.come:8080', // 代理跨域目标接口
      changeOrigin: true,
      secure: false, // 当代理某些https服务报错时用
      cookieDomainReweite: 'www.domain.com'  // 可以为false,表示不修改
    }],
    noInfo: true
  }
}
三、CORS 跨域资源共享

普通跨域请求:只服务端设置Access-Control-Allow-Origin即可,前端无须设置,若要带cookie请求:前后端都需要设置。

需注意的是:由于同源策略的限制,所读取的cookie为跨域请求借口所在的域cookie,而非当前页。

目前,所有浏览器都支持该功能(IE8+:IE8/9需要使用XDomainRequest对象来支持CORS),CORS也已经成为主流的跨域解决方案。

跨域请求并不是请求发不出去,请求实际上发出去了 服务器也给客户端响应了,浏览器不允许我们使用服务器的数据

此时可以让服务器通知浏览器,这个域名可以请求我的数据

1. 前端设置:
  1. 原生 ajax
// 前端设置是否带cookie
xhr.withCredentials = true;

示例代码:

var xhr = new XMLHttpRequest(); // IE8/9需要用window.XDomainRequest兼容
// 前端是否设置带cookie
xhr.withCredentials = true;
xhr.open('post','http://www.domain.com:8080/login',true);
xhr.setRequestHeader('Content-Type','application/x-www-form-urlencoded');
xhr.send('user=admin');
xhr.onReadystatechange = function() {
  if(xhr.readyState == 4 && xhr.status == 200) {
    alert(xhr.responseText);
  }
}

​ 2) jQuery ajax

$.ajax({
  ...
  xhrFields:{
    withCredentials: true // 前端设置是否带cookie
  }, 
  crossDomain: true, // 会让请求头中包含跨域的额外信息,但不会含cookie
  ...
})
  1. VUE框架

a) axios设置

axios.defaults.withCredentails = true

b) vue-resource设置

Vue.http.options.credentials = true
2. 服务端设置:

若后端设置成功,前端浏览器控制台则不会出现跨域报错信息,反之,则说明没设成功。

  1. php后端代码
<?php
	header('Access-Control-Allow-Origin: *');
	header('Access-Control-Request-Methods:GET,POST');
	header('Access-Control-Allow-Headers:x-requested-with,content-type,test-token,test-sessid');
?>

2)java后端代码

/*
*	导入包:import jacax.servlet.http.HttpServletResponse;
*	接口参数中定义:HttpServletResponse response
*/
// 允许跨域访问的域名:若有端口需写全(协议+域名+端口),若没有端口末尾不用加'/'
response.setHeader("Access-Control-Allow-Origin","http://www.domain.com");
// 允许前端带认证cookie:启用此项后,上面的域名不能为'*',必须指定具体的域名,否则浏览器会提示
response.setHeader("Access-Control-Allow-Credentials","true");
// 提示OPTIONS预检时,后端需要设置的两个常用自定义头
response.setHeader("Access-Control-Allow-Headers","Content-Type,X-Requested-With");
  1. Nodejs后台示例
var http = require('http');
var server = http.createServer();
var qs = require('querystring');

server.on('request', function(req, res) {
    var postData = '';

    // 数据块接收中
    req.addListener('data', function(chunk) {
        postData += chunk;
    });

    // 数据接收完毕
    req.addListener('end', function() {
        postData = qs.parse(postData);

        // 跨域后台设置
        res.writeHead(200, {
            'Access-Control-Allow-Credentials': 'true',     // 后端允许发送Cookie
            'Access-Control-Allow-Origin': 'http://www.domain1.com',    // 允许访问的域(协议+域名+端口)
            /* 
             * 此处设置的cookie还是domain2的而非domain1,因为后端也不能跨域写cookie(nginx反向代理可以实现),
             * 但只要domain2中写入一次cookie认证,后面的跨域接口都能从domain2中获取cookie,从而实现所有的接口都能跨域访问
             */
            'Set-Cookie': 'l=a123456;Path=/;Domain=www.domain2.com;HttpOnly'  // HttpOnly的作用是让js无法读取cookie
        });

        res.write(JSON.stringify(postData));
        res.end();
    });
});

server.listen('8080');
console.log('Server is running at port 8080...');

四、 nginx代理跨域

1、 nginx配置解决iconfont跨域

浏览器跨域访问js、css、img等常规静态资源被同源策略许可,但iconfont字体文件(eot|otf|ttf|woff|svg)例外,此时可在nginx的静态资源服务器中加入以下配置。

location / {
  add_header Access-Control-Allow-Origin *;
}
2、 nginx反向代理接口跨域

跨域原理: 同源策略是浏览器的安全策略,不是HTTP协议的一部分。服务器端调用HTTP接口只是使用HTTP协议,不会执行JS脚本,不需要同源策略,也就不存在跨越问题。

实现思路:通过nginx配置一个代理服务器(域名与domain1相同,端口不同)做跳板机,反向代理访问domain2接口,并且可以顺便修改cookie中domain信息,方便当前域cookie写入,实现跨域登录。

nginx具体配置:

#proxy服务器
server {
    listen       81;
    server_name  www.domain1.com;

    location / {
        proxy_pass   http://www.domain2.com:8080;  #反向代理
        proxy_cookie_domain www.domain2.com www.domain1.com; #修改cookie里域名
        index  index.html index.htm;

        # 当用webpack-dev-server等中间件代理接口访问nignx时,此时无浏览器参与,故没有同源限制,下面的跨域配置可不启用
        add_header Access-Control-Allow-Origin http://www.domain1.com;  #当前端只跨域不带cookie时,可为*
        add_header Access-Control-Allow-Credentials true;
    }
}

1.) 前端代码示例:

var xhr = new XMLHttpRequest();

// 前端开关:浏览器是否读写cookie
xhr.withCredentials = true;

// 访问nginx中的代理服务器
xhr.open('get', 'http://www.domain1.com:81/?user=admin', true);
xhr.send();

2.) Nodejs后台示例:

var http = require('http');
var server = http.createServer();
var qs = require('querystring');

server.on('request', function(req, res) {
    var params = qs.parse(req.url.substring(2));

    // 向前台写cookie
    res.writeHead(200, {
        'Set-Cookie': 'l=a123456;Path=/;Domain=www.domain2.com;HttpOnly'   // HttpOnly:脚本无法读取
    });

    res.write(JSON.stringify(params));
    res.end();
});

server.listen('8080');
console.log('Server is running at port 8080...');
posted @ 2021-06-15 18:27  春燕啄春泥  阅读(1231)  评论(0编辑  收藏  举报