跨域方式实现原理

 

跨域方式实现原理

 同源策略限制内容有:

  • Cookie、LocalStorage等存储性内容
  • AJAX 请求被浏览器拦截

但是有三个标签是允许跨域加载资源:

  •  <img src=xxx>
  • <link href=xxx>
  • <script src=xxx>

协议、子域名、主域名、端口号中任意一个不相同时,都算作不同域。不同域之间相互请求资源,就算作“跨域”

协议端口号 造成的跨域是前端无法解决的

跨域并不是请求发不出去,请求能发出去,服务端能收到请求并正常返回结果,只是结果被浏览器拦截了

跨域解决方案

1) JSONP

  原理:利用 <script> 标签没有跨域限制的漏洞,网页可以得到从其他来源动态产生的 JSON 数据。JSONP请求一定需要对方的服务器做支持才可以。

    优点:JSONP优点是简单兼容性好,可用于解决主流浏览器的跨域数据访问的问题

  缺点: 仅支持get方法,参数需要拼接起来,不够安全

H5

    function jsonp({
        url,
        params,
        callback
    }) {
        return new Promise((resolve, reject) => {
            let script = document.createElement('script')
            window[callback] = data => {
                resolve(data);
            }
            params = {
                ...params,
                callback
            }
            Object.keys(params).forEach(function (value, index) {
                script.src += index ? `&${value}=${params[value]}` : `${url}?${value}=${params[value]}`
            })
            document.body.appendChild(script)
        })
    }
    jsonp({
        url: 'http://localhost:3000/wel',
        params: {
            id: 3,
            name: 'Kobe'
        },
        callback: 'show'
    }).then((data) => {
        console.log(data)
    })

node服务端

var express = require('express');
var app = express();


// 接口
app.get('/', (req, res) => res.send('hello word!'));
app.get('/wel', (req, res) => {
  let {
    id,
    name,
    callback
  } = req.query
  let obj = {
    name: 'chen',
    height: 30,
    code: -1
  }
  if (id && name) {
    obj = {
      name: 'li',
      height: 50,
      code: 1
    }
  }
  return res.send(`${callback}(${JSON.stringify(obj)})`)
});

app.listen(3000, () => console.log('listening on port 3000'));

2) CORS(参考文章:HTTP访问控制(CORS))

  跨域资源共享( CORS )机制允许 Web 应用服务器进行跨域访问控制,从而使跨域数据传输得以安全进行。现代浏览器支持在 API 容器中(例如 XMLHttpRequest 或 Fetch )使用 CORS,以降低跨域 HTTP 请求所带来的风险。(MDN描述)

   功能描述 :跨域资源共享标准新增了一组 HTTP 首部字段,允许服务器声明哪些源站通过浏览器有权限访问哪些资源。另外,规范要求,对那些可能对服务器数据产生副作用的 HTTP 请求方法(特别是 GET 以外的 HTTP 请求,或者搭配某些 MIME 类型的 POST 请求),浏览器必须首先使用 OPTIONS 方法发起一个预检请求(preflight request),从而获知服务端是否允许该跨域请求。服务器确认允许之后,才发起实际的 HTTP 请求。在预检请求的返回中,服务器端也可以通知客户端,是否需要携带身份凭证(包括 Cookies 和 HTTP 认证相关数据)。

  node服务端

let whitList = ['http://localhost:8080','http://localhost:8081'] //设置白名单
app.use(function(req, res, next) {
  let origin = req.headers.origin
  console.log(origin)
  if (whitList.includes(origin)) {
    // 设置哪个源可以访问我
    res.setHeader('Access-Control-Allow-Origin', origin)
    // 允许携带哪个头访问我
    res.setHeader('Access-Control-Allow-Headers', 'x-requested-with')
    // 允许哪个方法访问我
    res.setHeader('Access-Control-Allow-Methods', 'GET,PUT,POST,DELETE')
    // 允许携带cookie
    res.setHeader('Access-Control-Allow-Credentials', true)
    // 预检的存活时间
    res.setHeader('Access-Control-Max-Age', 36)
    // 允许返回的头
    res.setHeader('Access-Control-Expose-Headers', 'x-requested-with')
    if (req.method === 'OPTIONS') {
      res.end() // OPTIONS请求不做任何处理
    }
  }
  next()
})
app.put('/index', function(req, res) {
  res.setHeader('x-requested-with', 'origin') //返回一个响应头,后台需设置
  const obj={
    id:1,
    name:'Kobe',
    code:1,
  }
  return res.send(`${JSON.stringify(obj)}`)
})

 

H5

    let xhr = new XMLHttpRequest();
      xhr.withCredentials = true; // 前端设置是否带cookie
      xhr.open("PUT", "http://localhost:3000/index", true);
      xhr.setRequestHeader("x-requested-with", "origin");
      xhr.onreadystatechange = function() {
        if (xhr.readyState === 4) {
          if ((xhr.status >= 200 && xhr.status < 300) || xhr.status === 304) {
            console.log(xhr.response);
            //得到响应头,后台需设置Access-Control-Expose-Headers
            console.log(xhr.getResponseHeader("x-requested-with"));
          }
        }
      };
      xhr.send();

 

 

  

 

posted @ 2019-08-20 18:30  大橘已定  阅读(616)  评论(0编辑  收藏  举报