AlexZ33 / lessions

自己练习的各种demo和课程
12 stars 2 forks source link

跨域问题总结 #67

Open AlexZ33 opened 4 years ago

AlexZ33 commented 4 years ago

cors

线上: Node.js如何设置允许跨域

开发环境:

nignx:

location ~ ^/video/(.*)$ {
        resolver        10.237.8.8;
        proxy_pass      http://$1;
    }

JSONP

有时我们需要给非本域的页面提供接口服务,又由于一些历史原因无法通过 CORS 实现,可以通过 JSONP 来进行响应。

/**
 * Module exports.
 */

module.exports = jsonp;

/**
 * Callback index.
 */

var count = 0;

/**
 * Noop function.
 */

function noop(){}

/**
 * JSONP handler
 *
 * Options:
 *  - param {String} qs parameter (`callback`)
 *  - prefix {String} qs parameter (`__jp`)
 *  - name {String} qs parameter (`prefix` + incr)
 *  - timeout {Number} how long after a timeout error is emitted (`60000`)
 *
 * @param {String} url
 * @param {Object|Function} optional options / callback
 * @param {Function} optional callback
 */

function jsonp(url, opts, fn){
  if ('function' == typeof opts) {
    fn = opts;
    opts = {};
  }
  if (!opts) opts = {};

  var prefix = opts.prefix || '__jp';

  // use the callback name that was passed if one was provided.
  // otherwise generate a unique name by incrementing our counter.
  var id = opts.name || (prefix + (count++));

  var param = opts.param || 'callback';
  var timeout = null != opts.timeout ? opts.timeout : 60000;
  var enc = encodeURIComponent;
  var target = document.getElementsByTagName('script')[0] || document.head;
  var script;
  var timer;

  if (timeout) {
    timer = setTimeout(function(){
      cleanup();
      if (fn) fn(new Error('Timeout'));
    }, timeout);
  }

  function cleanup(){
    if (script.parentNode) script.parentNode.removeChild(script);
    window[id] = noop;
    if (timer) clearTimeout(timer);
  }

  function cancel(){
    if (window[id]) {
      cleanup();
    }
  }

  window[id] = function(data){
    cleanup();
    if (fn) fn(null, data);
  };

  // add qs component
  url += (~url.indexOf('?') ? '&' : '?') + param + '=' + enc(id);
  url = url.replace('?&', '?');

  // create script
  script = document.createElement('script');
  script.src = url;
  target.parentNode.insertBefore(script, target);

  return cancel;
}
AlexZ33 commented 4 years ago

跨域是浏览器行为

关闭浏览器跨域限制 (不推荐,会有安全问题)

open -a /Applications/Google\ Chrome.app --args --disable-web-security --user-data-dir

种类

1、 通过jsonp跨域
2、 document.domain + iframe跨域
3、 location.hash + iframe
4、 window.name + iframe跨域
5、 postMessage跨域
6、 跨域资源共享(CORS)
7、 nginx代理跨域
8、 nodejs中间件代理跨域(http-proxy-middleware)
9、 WebSocket协议跨域

jsonp

需要后端单独支持,已经不推荐使用了

// JSONP

        function createJsonp() {

            var script = document.createElement("script"),

                timeName = new Date().getTime() + Math.round(Math.random() * 1000),

                callback = "JSONP_" + timeName;

            window[callback] = function(data) {

                clearTimeout(timeout_flag);

                document.body.removeChild(script);

                success(data);

            }

            script.src = url + (url.indexOf("?") > -1 ? "&" : "?") + "callback=" + callback;

            script.type = "text/javascript";

            document.body.appendChild(script);

            setTime(callback, script);

        }

        //设置请求超时

        function setTime(callback, script) {

            if (timeOut !== undefined) {

                timeout_flag = setTimeout(function() {

                    if (dataType === "jsonp") {

                        delete window[callback];

                        document.body.removeChild(script);

                    } else {

                        timeout_bool = true;

                        xhr && xhr.abort();

                    }

                    console.log("timeout");

                }, timeOut);

            }

        }

proxy

react

 1):安装http-proxy-middleware管理包,cnpm http-proxy-middleware --save

const proxy = require(‘http-proxy-middleware‘);

module.exports = function(app) {
  app.use(proxy(‘/api‘, { 
       target: ‘http://192.168.1.144:8181‘ ,
       secure: false,
       changeOrigin: true,
       pathRewrite: {
        "^/api": "/"
       },
       // cookieDomainRewrite: "http://localhost:3000"
    }));
};

vue.config.js

devServer: {
        host: '0.0.0.0',
        port: 8081,
        https: false,
        hotOnly: false,
        disableHostCheck: true,
        // proxy: {
        //   '/api': {
        //     target: 'https://m.youpin.mi.com',
        //     ws: true,
        //     changeOrigin: true,
        //     pathRewrite: {
        //         '^/api': '/api', // rewrite path
        //     },
        //     headers: {
        //         Origin: 'https://m.youpin.mi.com',
        //         Referer: 'https://m.youpin.mi.com',
        //         Cookie: 'beegosessionID=94c74e9ad23'
        //     }
        //   },
}

nginx

终极解决方案,

server {
        listen 8092;
        server_name aa.app.xiaomiyoupin.com;

                error_log /usr/local/etc/nginx/logs/shop.error.log;
                access_log /usr/local/etc/nginx/logs/shop.access.log;
        # 前端页面走本地(vue-cli开的server)
        location / {
            proxy_pass http://localhost:8081;
        }

        # 后端接口都是/api开头的
        location /api {
            proxy_http_version 1.1;
            proxy_set_header   X-Real-IP        $remote_addr;
            proxy_set_header   X-Forwarded-For  $proxy_add_x_forwarded_for;
            # proxy_set_header   X-NginX-Proxy    true;
            # proxy_set_header   Connection "";
            proxy_set_header   Host             app.xiaomiyoupin.com;
            # proxy_pass http://st.shop.youpin.srv;
            proxy_pass https://m.xiaomiyoupin.com;
        }
        location ~ ^/app/shopv3/ {

            proxy_set_header Cookie 'beegosessionID=94c74e9ad2349278b5a67fa17076230e; b_auth=mijiayoupin; serviceToken=cTww0DUseqPODIcT5cvyy9nNT+yBPhcaVfZcWmmlV5N6ajVrta+HoJPzdbhB08jVYVbFX5bNseY2MJitcQED7x1k3CoCAApFfaXeCC7n45Iqn7OSkvIr7spQqX4QGIQXGxwg9Ddn8zW/w4Wjc4qssQ==; cUserId=DKtjN6Lk5SugGBs04CbYODo2nsI; YPToken=4PberWbMNP6-TSzQkMdASH5N5PVDEObS0YFnL-h91PwlqjBU7QO5k5wZ7AKfUE3h';

            proxy_set_header Referer 'https://m.xiaomiyoupin.com';
            # proxy_set_header Host $host;
            proxy_pass https://m.xiaomiyoupin.com;
        }

    }

cors

每次都发送两次请求,并发量比较大的时候,对服务器压力比较大

app.use('*',function (req, res, next) {
    res.header('Access-Control-Allow-Origin', '*'); //这个表示任意域名都可以访问,这样写不能携带cookie了。
//   res.header('Access-Control-Allow-Origin', 'http://www.baidu.com'); //这样写,只有www.baidu.com 可以访问。
    res.header('Access-Control-Allow-Headers', 'Content-Type, Content-Length, Authorization, Accept, X-Requested-With , yourHeaderFeild');
    res.header('Access-Control-Allow-Methods', 'PUT, POST, GET, DELETE, OPTIONS');//设置方法
    if (req.method == 'OPTIONS') {
      res.send(200); // 意思是,在正常的请求之前,会发送一个验证,是否可以请求。
    }
    else {
      next();
    }
});

完整的server例子

var express = require('express')

var app = express()

// var cors = require('cors')
// app.use(cors())
// var corsOptions = {
//   origin: 'http://www.baidu.com', //只有百度可以访问
//   optionsSuccessStatus: 200 
// }

// app.get('/products/:id', cors(corsOptions), function (req, res, next) {
//   res.json({msg: '只有百度可以访问'})
// })

app.use('*',function (req, res, next) {
    res.header('Access-Control-Allow-Origin', '*'); //这个表示任意域名都可以访问,这样写不能携带cookie了。
//   res.header('Access-Control-Allow-Origin', 'http://www.baidu.com'); //这样写,只有www.baidu.com 可以访问。
    res.header('Access-Control-Allow-Headers', 'Content-Type, Content-Length, Authorization, Accept, X-Requested-With , yourHeaderFeild');
    res.header('Access-Control-Allow-Methods', 'PUT, POST, GET, DELETE, OPTIONS');//设置方法
    if (req.method == 'OPTIONS') {
      res.send(200); // 意思是,在正常的请求之前,会发送一个验证,是否可以请求。
    }
    else {
      next();
    }
});
app.get('/',function(req,res){
    res.send('hello')
})
app.post('/api2',function(req,res){
    // console.log(req)
    res.json({
        "data":1
    })
    // res.send({some:'json'});
})
app.get('/api3',function(req,res){
    // console.log(req)
    res.jsonp({
        "data":1
    })
    // res.send({some:'json'});
})

app.listen(3000, function () {
  console.log('CORS-enabled web server listening on port 3000')
})
AlexZ33 commented 4 years ago

跨域踩坑经验总结(内涵:跨域知识科普)