什么是跨域,怎么解决跨域?

522 阅读7分钟

小知识,大挑战!本文正在参与“程序员必备小知识”创作活动。

1、什么是跨域

演示跨域:

把昨天的ajax访问node.js编写的后台server.js

然后注释掉server.js中的允许跨域代码

server.js:

/* 
实现注册登录
接口使用:
​
接口地址:http://127.8.0.1: 3001/user接受参数:
act: login/登录   reg/注册
user:用户名
pass:密码
​
*/
​
​
var http = require('http');
var fs = require('fs');
var querystring = require('querystring');
var url = require('url');
var user = {} ;//act 判断登录还是注册,user /pass
var server = http.createServer(function (req, res) {
  //设置允许跨域的域名,*代表允许任意域名跨域
  /* res.setHeader(
    'Access-Control-Allow-Origin',"*"
  ) */
  const GET = url.parse(req.url, true);
  var query = GET.query;
  var pathname = GET.pathname;
  var str = '';
  if (pathname == '/user') {
    req.on('data', function (chunk) {
      console.log(11)
      str += chunk;
    });
    req.on('end', function () {
      var POST = querystring.parse(str)
      switch (POST.act) {
        case 'reg':
          if (user[POST.user] == null) {
            user[POST.user] = POST.pass;
            res.write('{"ok":true,"msg":"注册成功"}')
          } else {
            res.write('{"ok":false,"msg":"该用户名已经被注册"}')
          }
          break;
        case 'login':
          if (user[POST.user] == null) {
            res.write('{"ok":false,"msg":"用户名不存在"}')
          } else if (user[POST.user] != POST.pass) {
            res.write('{"ok":false,"msg":"用户名或密码不对"}')
          } else {
            res.write('{"ok":true,"msg":"登录成功"}')
          }
          break;
        default:
          res.write('你要干啥?')
      }
      res.end()
    })
  } else {
    const file_name = './www' + req.url;
    fs.readFile(file_name, 'utf-8', function (err, data) {
      if (err) {
        res.write('404')
      } else {
        res.write(data)
      }
      res.end()
    })
  }
}).listen(3001, 'localhost',function () {
  console.log('your server is running here:lhocalhost:3001')
})

login_server_demo.html:

<!DOCTYPE html>
<html lang="en"><head>
    <meta charset="UTF-8">
    <title>Document</title>
</head><body>
    用户名:<input type="text" name="user" id="user">
    <br>
    密码: <input type="password" name="pass" id="pass">
​
    <br>
    <input type="submit" value="注册" onclick="register()">
    <input type="submit" value="登录" onclick="login()">
</body><script>
    function register() {
        var user_  = document.getElementById('user').value;
        var pass_  = document.getElementById('pass').value;
​
        //1、创建对象
        var ajax_ = new XMLHttpRequest() || new ActiveXObject('Microsoft.XMLHTTP');
        //2、创建连接
        ajax_.open('post','http://127.0.0.1:3001/user',true);
        //3、发送请求
        //拼接数据
       
        var data = 'act=reg&user='+user_+'&pass='+pass_;
        ajax_.send(data);
        //4、接收服务器数据
        ajax_.onreadystatechange = function(){
            console.log(ajax_.readyState);
            if(ajax_.readyState==4){
                
                if(ajax_.status==200){
                    console.log(ajax_.responseText);
                }else{
                    console.log('erro');
                }
            }
        }
​
    }
​
​
    // 登录部分
    function login(){
        var user = document.getElementById('user').value;
        var pass = document.getElementById('pass').value;
​
​
        var ajax_ = new XMLHttpRequest()|| new ActiveObject('Microsoft.XMLHTTP');
        ajax_.open('post','http://127.0.0.1:3001/user',true);
        
        var date = 'act=login&user='+user+'&pass='+pass;
        ajax_.send(date);
​
        ajax_.onreadystatechange=function(){
            if(ajax_.readyState==4){
                if(ajax_.status==200){
                    console.log(ajax_.responseText);
                }else{
                    console.log('erro');
                }
            }
        }
​
    }
​
​
</script></html>

此时显示错误信息如下:

该错误表示:

从原产地“http://127.0.0.1:5500”访问“http://127.0.0.1:3001/user”的XMLHttpRequest已被CORS策略阻止:被请求的资源上没有“Access- control - allow - origin”报头。

CORS:

CORS是一个w3c标准,全称是:跨域资源共享(Cross-origin resource sharing)。它允许浏览器向跨源服务器,发出XMLHttpRequest请求,从而克服了AJAX只能同源使用的限制

跨域,指的是浏览器不能执行其他网站的脚本 简单地理解就是因为JavaScript同源策略的限制,a.com 域名下的js无法操作b.com或是c.a.com域名下的对象 例子:比如淘宝网不能请求京东的数据

产生跨域的原因:

由浏览器的同源策略造成的

同源策略,它是由Netscape提出的一个著名的安全策略。 现在所有支持JavaScript 的浏览器都会使用这个策略。

同源策略大白话:

生活中,社会能够安定的运行,有一个前提,大家都默认遵守一个协定:每个人只能自由的出入自己的家,拿取自己家的东西。而如果大家都不遵循这种协定,你可以随便去拿别人家的东西,别人也可以随便的出入你的家,这样社会就乱套了。此处大家都遵循的各用各家的东西,就叫做同源策略

所谓同源是指,域名,协议,端口相同

域名和ip地址:

比方说,我们要去逛永辉超市,在导航里面输入永辉超市,导航会找到永辉超市的地址,在某某街某某号,并导航到这个位置。

这里我们输入永辉超市就好比输入一个域名,但是系统会将它转换为具体的街道门牌号,这个就好比网络上的ip地址,是精确且唯一的。

判断以下内容是否能够正常访问:

2、跨域的限制

随着互联网的发展,同源政策越来越严格。目前,如果非同源,共有三种行为受到限制。

  • 无法读取非同源网页的 Cookie、LocalStorage 和 IndexedDB。【缓存数据】
  • 无法接触非同源网页的 DOM。
  • 无法向非同源地址发送 AJAX 请求(可以发送,但浏览器会拒绝接受响应)

查看LocalStorage:

右键 ,检查,application,localStroage

3、跨域解决方案

1、设置CORS跨域资源共享

后台在请求头信息里面添加:服务端:、

   response.setHeader("Access-Control-Allow-Origin", "*");

“*”表示所有的域都可以接受 CORS 需要浏览器和服务器同时支持。目前,所有浏览器都支持该功能。

2、Ajax 跨域请求

由于在工作中需要使用AJAX请求其他域名下的资源,但是会出现拒绝访问的情况,这是因为基于安全的考虑,AJAX只能访问本地,同域的资源,而不能跨域访问 可以让服务器去别的网站获取内容然后返回页面

大白话:

我们要租房子,房源被中介给垄断了,我们无法直接找到房东。

我们先找到中介,中介去和房东谈具体事宜,谈好以后把每个月多少钱的信息告诉我们

3、Jsonp

例:

通过img的src属性,访问百度的图片:

 <img src="https://ss0.bdstatic.com/70cFuHSh_Q1YnxGkpoWK1HF6hhy/it/u=1910846161,216642357&fm=26&gp=0.jpg" alt="">

jsonp就是利用script标签的跨域能力请求资源

Jsonp 实现思路:

  • 全称是 JSON with Padding,请求时通过动态创建一个 Script,在 Script 中发出请求,
  • 通过这种变通的方式让请求资源可以跨域。
  • 它不是一个官方协议,是一个约定,约定请求的参数里面如果包含指定的参数(默认是 callback),就说明是一个 JSONP 请求,服务器发现是 JSONP 请求,就会把原来的返回对象变成 JS 代码。
  • JS 代码是函数调用的形式,它的函数名是 callback 的值,它的函数的参数就是原来需要返回的结果。
  • 后台会把函数调用,重新返回给前端

json_01.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>模拟jsonp</title>
</head>
<body>
   <script>
    //    调用后台函数
       function fn(value){
           console.log(value);
       }
   </script>
   <!-- 
       通过script的src属性进行跨域
       传递的参数默认是callback
       后台看到callback就能够判断出来是一个jsonp请求
       会根据callback的值,自动生成函数,函数名就是callback的值
       后台把数据封装成对象,作为参数传递给函数作为值
    -->
   <script src="./testJs.js?callback=fn"></script>
</body>
</html>

testjs.js

// testjs.js就是我们模拟的后台
// 后台能判断出来是否是jsonp请求
// 如果是,就会生成一个函数的调用,而函数名,就是前端提交上来的callback的值在假如我们要给前端返回一个数据
​
​
​
var data = {
    "name":"张三",
    age : 20,
    address:"郑州"
}
​
// 在后台生成了函数的调用,把数据以参数的形式,进行传递
// 后台会把这个函数的调用,重新返回给前端
fn(data);
​

使用node.js模拟jsonp

1、找到后台文件,jsonp_server.js,复制进项目

/* 
jsonp 接口地址:
http: //localhost:3003/jsonp?callback=test
传参数
约定请求的参数中的指定参数:callback
服务器发现是 JSONP 请求, 就会把原来的返回对象变成 JS 代码。
JS 代码是函数调用的形式, 它的函数名是 callback 的值, 
它的函数的参数就是原来需要返回的结果。
​
*/
​
​
//通过require将http库包含到程序中
let http = require('http');
//引入url模块解析url字符串
let url = require('url');
//引入querystring模块处理query字符串
let querystring = require('querystring');
//创建新的HTTP服务器
let server = http.createServer();
//通过request事件来响应request请求
server.on('request', function (req, res) {
  let urlPath = url.parse(req.url).pathname;
  let qs = querystring.parse(req.url.split('?')[1]);
​
  // 如果请求路径里面包含有jsonp 和callback 就判断出来这是一个jsonp请求
  if (urlPath === '/jsonp' && qs.callback) {
    res.writeHead(200, {
      'Content-Type': 'application/json;charset=utf-8'
    });
​
    // 此处的data数据 就是后台要返回个前端的数据
    let data = {
      "msg": "jsonp返回的数据",
      "fn": qs.callback
    };
    data = JSON.stringify(data);
    // 根据callback的值,创建函数 data 是函数内传递的参数
    let callback = qs.callback + '(' + data + ');';
    console.log(callback);
    // 向前端进行响应
    res.end(callback);
  } else {
    res.writeHead(200, {
      'Content-Type': 'text/html;charset=utf-8'
    });
    res.end('Hell World\n');
  }
});
server.listen(3003,'localhost',function(){
  console.log('your server is running here:localhost:3003');
});

2、使用node命令启动文件

node jsonp_server.js

3、创建html文件,jsonp_server.html

<!DOCTYPE html>
<html lang="en"><head>
    <meta charset="UTF-8">
    <title>Document</title>
</head><body>
    <script>
        function test(v){
            console.log(v);
        }
    </script>
    <!-- callback:是后台定好的,如果规定是其他的,例如backcall / cb你在传参数是,就要按规定来,传bcakcall /cb
        test:具体的参数的值,可以根据后台的规定来定义
        例如:后台规定参数的值,必须叫test那你就只能写testI -->
​
    <script src="http://localhost:3003/jsonp?callback=test"></script></body></html>

效果图:

JSONP优缺点:

优点:兼容性强&不受同源策略的限制 缺点:只能用get方法,不能使用post方法

因为get请求方式把请求参数放在了url地址中,后台解析jsonp是通过url的callback参数进行判断的,所以之支持get请求

优化jsonp.html,动态创建script的src属性:

<!DOCTYPE html>
<html lang="en"><head>
    <meta charset="UTF-8">
    <title>Document</title>
</head><body>
​
    <button onclick="getJsonp()">获取jsonp</button>
​
 
    <script>
        function test(v){
            console.log(v);
        }
​
        function getJsonp(){
            var script_ = document.createElement('script');
            script_.src = 'http://localhost:3003/jsonp?callback=test';
            document.body.appendChild(script_);
​
            //清除页面上的script标签
            if(script_){
                document.body.removeChild(script_);
            }
        }
    </script>
​
​
​
</body></html>

4、WebSocket 【了解】

WebSocket 是一种通信协议,和http,https是同一级别的。

使用ws://(非加密)和wss://(加密)作为协议前缀。

该协议不实行同源政策,只要服务器和浏览器支持,就可以通过它进行跨源通信。 缺点 只有在支持websocket协议的服务器和浏览器上才能正常工作

websocket不支持ie6以下的浏览器

webSocket在线测试网站:

www.websocket-test.com/

菜鸟教程详解:

www.runoob.com/html/html5-…