一、同源策略

什么是同源策略?

同源策略(Same-origin Policy):为了保证浏览器的信息安全,浏览器采用同源策略,保证当前源中的资源只能在当前的源中使用;其他源如果需要使用当前源资源,需要特殊技术,这种A源访问B源的资源的通信称为跨域;

示例:

  1. let xhr = new XMLHttpRequest();
  2. xhr.open('GET', 'https://www.baidu.com/', true);
  3. xhr.onreadystatechange = function () {
  4. if (xhr.readyState === 4 && xhr.status === 200) {
  5. console.log('xxx')
  6. }
  7. };
  8. xhr.send();
  • 以上请求会报错:
  1. Access to XMLHttpRequest at 'https://www.baidu.com/' from origin 'http://localhost:63342' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.

当出现以上错误时说明你正在进行一个跨域的操作;

同源策略的要求:

同源策略要求通信的两个源的协议、域名、端口号要相同,如果三者中任意一个不同就是不满足同源策略;不满足同源策略的通信就是跨域;

常用的跨域解决方案:

  1. JSONP:利用 script 的 src 属性是不受同源策略约束的,可以访问不同服务器或者端口号下的数据
  2. 服务端转发,因为同源策略只在客户端存在,在服务端是不存在的;所以可以由服务端转发请求;webpack-dev-server的proxy
  3. nginx 转发,nginx 是服务器应用程序,它可以接受客户端的请求,然后根据规则可以配置自动转发;
  4. CORS: Cross-Origin-Resource-Sharing: 需要目标域设置 Access-Control-Allow-Origin 头信息;
  5. postMessage: 发送window.postMessage(); 接收window.onMessage()
  6. webSocket协议跨域:
    1. socket.on(‘connect’)
    2. socket.on(‘message’)
    3. socket.on(‘disconnect’)
    4. socket.send()
  7. document.domain + iframe
  8. window.name + iframe
  9. location.hash + ifram

    二、JSONP

JSONP 是一种常用的解决跨域的方式;

原理

利用 script 的 src 属性是不受同源策略约束的,可以访问不同服务器或者端口号下的数据

  1. 提前声明一个叫做 fn 的函数,给 fn 设置一个形参;
  2. 在页面给 script 的 src 的指向的路径拼接一个 callback 属性,callback=fn;当浏览器解析到这个 script 标签时,会向 src 指向的路径发起 http 请求;
  3. 服务器收到这个请求后,会返回一个 fn (这里面是服务器返回的数据)
  4. fn({xxx}) 这个是让 fn 执行,小括号里面就是服务器发送给我们的数据

示例

JS代码:

  1. function fn(data) {
  2. console.log(data);
  3. }

HTML代码

  1. <script src="http://matchweb.sports.qq.com/kbs/calendar?columnId=100000&callback=fn"></script>

通过调用淘宝搜索接口的案例

  1. <input type="text" id="search">
  2. <input type="button" value="搜索" id="button">
  3. <div id="app">test jsonp</div>
  4. <script type="text/javascript">
  5. const search = document.getElementById("search");
  6. const button = document.getElementById("button");
  7. const app = document.getElementById("app");
  8. let searchSomething = ""
  9. button.addEventListener("click", function (e) {
  10. searchSomething = search.value
  11. seachFn();
  12. })
  13. //添加<script>标签的方法
  14. function addScriptTag(src) {
  15. var script = document.createElement('script');
  16. script.setAttribute("type", "text/javascript");
  17. script.src = src;
  18. document.body.appendChild(script);
  19. }
  20. function seachFn() {
  21. //搜索taobao,将自定义的回调函数名 cb 传入callback参数中
  22. addScriptTag(`https://suggest.taobao.com/sug?code=utf-8&q=${searchSomething}&callback=cb`);
  23. }
  24. //自定义的回调函数 cb
  25. function cb(data) {
  26. //我们就简单的获取 taobao 搜索结果的第一条记录中url数据
  27. console.log(data);
  28. app.innerText = JSON.stringify(data.result);
  29. }
  30. </script>

使用node搭建http服务实现jsonp

创建node服务

  1. const http = require('http');
  2. //引入url-parse模块解析url字符串
  3. const urlParse = require('url-parse');
  4. // 引入query-string模块解析url.query字符串
  5. const queryString = require('query-string');
  6. //创建新的HTTP服务器
  7. const server = http.createServer();
  8. //通过request事件来响应request请求
  9. server.on('request',function(req, res){
  10. const url = urlParse(req.url)
  11. var urlPath = queryString.parse(url.query);
  12. //如果urlPath为'jsonp',就认定该请求为携带jsonp方法的http请求
  13. if(url.pathname === '/jsonp'){
  14. res.writeHead(200,{'Content-Type':'application/json;charset=utf-8'});
  15. var data = {
  16. "name": "jsonp data"
  17. };
  18. data = JSON.stringify(data);
  19. //前端的回调函数名为urlPath.callback
  20. var callback = urlPath.callback+'('+data+');'; // 定义的callback供前端使用
  21. res.end(callback);
  22. }
  23. else{
  24. res.writeHead(200, {'Content-Type':'text/html;charset=utf-8'});
  25. res.end('Hell World\n');
  26. }
  27. });
  28. //监听8080端口
  29. server.listen('8123',() => {
  30. //用于提示我们服务器启动成功
  31. console.log('Server running port 8123!');
  32. });

运行 node server.js,启动http://localhost:8123服务
新建前端html页面

  1. <body>
  2. <script type="text/javascript">
  3. //添加<script>标签的方法
  4. function addScriptTag(src) {
  5. var script = document.createElement('script');
  6. script.setAttribute("type", "text/javascript");
  7. script.src = src;
  8. document.body.appendChild(script);
  9. }
  10. window.onload = function() {
  11. //搜索taobao,将自定义的回调函数名 cb 传入callback参数中
  12. addScriptTag(`http://localhost:8123/jsonp?callback=hello`);
  13. }
  14. function hello(data) {
  15. console.log(data);
  16. }
  17. </script>
  18. </body>