前端跨域總結(jié)(持續(xù)更新)

同源策略(Same origin policy)是一種約定羹与,它是瀏覽器最核心也最基本的安全功能氮采,如果缺少了同源策略塔插,則瀏覽器的正常功能可能都會受到影響∨雌椋可以說Web是構(gòu)建在同源策略基礎(chǔ)之上的几缭,瀏覽器只是針對同源策略的一種實現(xiàn)。現(xiàn)在所有支持JavaScript 的瀏覽器都會使用這個策略沃呢。所謂同源是指年栓,域名,協(xié)議薄霜,端口相同某抓。當一個瀏覽器的兩個tab頁中分別打開來 百度和谷歌的頁面當瀏覽器的百度tab頁執(zhí)行一個腳本的時候會檢查這個腳本是屬于哪個頁面的纸兔,即檢查是否同源,只有和百度同源的腳本才會被執(zhí)行否副。如果非同源汉矿,那么在請求數(shù)據(jù)時,瀏覽器會在控制臺中報一個異常备禀,提示拒絕訪問负甸。
??源于同源策略,我們有時需要跨域處理痹届。

一呻待、jsonp解決方案

jsonp是利用script標簽沒有跨域限制的特性,通過在src的url的參數(shù)上附加回調(diào)函數(shù)名字队腐,然后服務(wù)器接收回調(diào)函數(shù)名字并返回一個包含數(shù)據(jù)的回調(diào)函數(shù)
function doSomething(data) {
  // 對data處理
}
var script = document.createElement("script");
script.src = "http://www.b.com/b.html?callback=doSomething";
document.body.appendChild(script);

// 1.生成一個script標簽蚕捉,將其append在body上,向服務(wù)器發(fā)出請求
// 2.服務(wù)器根據(jù) callback 這個參數(shù)生成一個包含數(shù)據(jù)的函數(shù) doSomething({"a", "1"})
// 3.頁面事先已聲明doSomething函數(shù)柴淘,此時執(zhí)行 doSomething(data) 這個函數(shù)迫淹,獲得數(shù)據(jù)


示例
前端以jq封裝的jsonp為例
        $.ajax({
             url:'http://192.168.9.5/jsonp_test1.jsp',
             dataType:"jsonp",
             jsonp:"jsonpcallback",
             success:function(data){
                 var $ul = $("<ul></ul>");
                 $.each(data,function(i,v){
                     $("<li/>").text(v["id"] + " " + v["name"]).appendTo($ul)
                 });
                 $("#res").append($ul);
             }
        });


服務(wù)器的處理
c#
[HttpGet]
        public ActionResult getSurvey_Contact(String contact_id,string survey_id)
        {
            var callback = Request.QueryString["callback"];   //獲取回調(diào)函數(shù)的名字
            var contactColl = MyMongoDb.CurrentDB.GetCollection("Survey_Contact");
            if (contact_id == null)
                contact_id = String.Empty;

            var doc = contactColl.FindAs<BsonDocument>(Query.And(Query.EQ("contact_id", contact_id), Query.EQ("Survey_Id", survey_id))).FirstOrDefault();
            if (doc == null)
                doc = new BsonDocument();
            FormateJSON(new[] { doc });
            var answerColl = MyMongoDb.CurrentDB.GetCollection("Survey_Answers");
            var answerDocs = answerColl.FindAs<BsonDocument>(Query.And(Query.EQ("contact_id", contact_id), Query.EQ("survey_id", survey_id))).ToArray();
            FormateJSON(answerDocs);
            doc.Set("answers", new BsonArray(answerDocs));
            var result = new ContentResult();
            result.ContentEncoding = System.Text.Encoding.UTF8;
            result.ContentType = "application/json";
            //Regex reg = new Regex("ObjectId\\(([a-z0-9])\\)");
            if (String.IsNullOrWhiteSpace(callback))
            {
                result.Content = doc.ToJson();
                return result;
            }
            else
            {
                result.Content = callback + "(" + doc.ToJson() + ");";   //前端用js文件格式解析結(jié)果,通過callback(data)獲取數(shù)據(jù)
                return result;
            }
        }

//題外
jquery的$.Jsonp或者$.ajax的jsonp和ajax沒有關(guān)系为严,只是jquery封裝起來方便使用敛熬。
實際也是利用script標簽沒有跨域限制的特性實現(xiàn)的

JSONP的優(yōu)點:它不像XMLHttpRequest對象實現(xiàn)的Ajax請求那樣受到同源策略的限制;它的兼容性更好第股,在更加古老的瀏覽器中都可以運行应民,不需要XMLHttpRequest或ActiveX的支持;并且在請求完畢后可以通過調(diào)用callback的方式回傳結(jié)果夕吻。

JSONP的缺點:它只支持GET請求而不支持POST等其它類型的HTTP請求诲锹;它只支持跨域HTTP請求這種情況,不能解決不同域的兩個頁面之間如何進行JavaScript調(diào)用的問題涉馅。

二归园、window.name+iframe

//window.name的原理是利用同一個窗口在不同的頁面共用一個window.name,這個需要在a.com下建立一個代理文件c.html,使同源后a.html能獲取c.html的window.name
//需要目標服務(wù)器響應(yīng)window.name

三稚矿、window.location.hash+iframe

//需要目標服務(wù)器作處理
//b.html將數(shù)據(jù)以hash值的方式附加到c.html的url上庸诱,在c.html頁面通過location.hash獲取數(shù)據(jù)后傳到a.html(這個例子是傳到a.html的hash上,當然也可以傳到其他地方)

四晤揣、document.domain

這種方式適用于主域相同桥爽,子域不同,比如http://www.a.com和http://b.a.com
假如這兩個域名下各有a.html 和b.html,

a.html

    document.domain = "a.com";
    var iframe = document.createElement("iframe");
    iframe.src = "http://b.a.com/b.html";
    document.body.appendChild(iframe);
    iframe.onload = function() {
        console.log(iframe.contentWindow....); // 在這里操作b.html里的元素數(shù)據(jù)
    }

b.html

    document.domain = "a.com";

注意:document.domain需要設(shè)置成自身或更高一級的父域碉渡,且主域必須相同聚谁。

五、html5的 postMessage+ifrme

//這個需要目標服務(wù)器或者說是目標頁面寫一個postMessage滞诺,主要側(cè)重于前端通訊形导。

假設(shè)在a.html里嵌套個<iframe src=""http://www.b.com/b.html" rel="nofollow">http://www.b.com/b.html" frameborder="0"></iframe>,在這兩個頁面里互相通信

a.html

    window.onload = function() {
        window.addEventListener("message", function(e) {
            alert(e.data);
        });

        window.frames[0].postMessage("b data", "http://www.b.com/b.html");
    }

b.html

    window.onload = function() {
        window.addEventListener("message", function(e) {
            alert(e.data);
        });
        window.parent.postMessage("a data", "http://www.a.com/a.html");
    }

這樣打開a頁面就先彈出 a data,再彈出 b data

六环疼、CORS解決方案

CORS是XMLHttpRequest Level 2 里規(guī)定的一種跨域方式。在支持這個方式的瀏覽器里朵耕,javascript的寫法和不跨域的ajax寫法一模一樣炫隶,只要服務(wù)器需要設(shè)置Access-Control-Allow-Origin: *

1、apache設(shè)置header : Access-Control-Allow-Origin

//httpd.conf
找到這行
#LoadModule headers_module modules/mod_headers.so
把#注釋符去掉
LoadModule headers_module modules/mod_headers.so
目的是開啟apache頭信息自定義模塊

<Directory />
AllowOverride none
Require all denied
</Directory>

改為下面代碼

<Directory />
Require all denied
Header set Access-Control-Allow-Origin *
</Directory>

設(shè)置請求頭

2阎曹、nginx設(shè)置header : Access-Control-Allow-Origin

在nginx的conf文件中加入以下內(nèi)容:
location / {
  add_header Access-Control-Allow-Origin *;
}

3伪阶、服務(wù)器設(shè)置header :Access-Control-Allow-Origin

//示例
php
//header("Access-Control-Allow-Origin:*");
//header("Access-Control-Allow-Origin:http://www.a.com");

c#
//在Web.config設(shè)置
    <httpProtocol>
      <customHeaders>
        <add name="Access-Control-Allow-Origin" value="*" />
        <add name="Access-Control-Allow-Headers" value="Content-Type" />
        <add name="Access-Control-Allow-Methods" value="GET, POST, PUT, DELETE, OPTIONS" />
      </customHeaders>
    </httpProtocol>


nodejs

var express = require('express');
var app = express();
//設(shè)置跨域訪問
app.all('*', function(req, res, next) {
    res.header("Access-Control-Allow-Origin", "*");
    res.header("Access-Control-Allow-Headers", "X-Requested-With");
    res.header("Access-Control-Allow-Methods","PUT,POST,GET,DELETE,OPTIONS");
    res.header("X-Powered-By",' 3.2.1')
    res.header("Content-Type", "application/json;charset=utf-8");
    next();
});

app.get('/getdata', function(req, res) {
    res.send({id:req.params.id, name: req.params.password});
});

app.listen(3000);
console.log('Listening on port 3000...');



//題外
如果需要跨域設(shè)置cookie,要設(shè)置Access-Control-Allow-Credentials处嫌,例如

c#
   x.Headers.Add("Access-Control-Allow-Origin", "http://localhost:8080");
   x.Headers.Add("Access-Control-Allow-Credentials", "true");

//題外
cors缺點:
需要服務(wù)器配合
設(shè)置Access-Control-Allow-Origin 存在一定的風險

七栅贴、chrome插件跨域方案

//CORS Toggle

八、代理服務(wù)器解決方案

1熏迹、nignx反向代理

//搭建一個中轉(zhuǎn)nginx服務(wù)器轉(zhuǎn)發(fā)請求,如果是自己的主機就比較好操作檐薯。但是用了Nginx配置之后,webpack的hot reload會存在比較大的延遲
//nginx.conf
http {
    include       mime.types;
    default_type  application/octet-stream;
    server {
        listen       8080;
        charset utf-8;
        access_log on;
        access_log  logs/host.access.log  ;
        location / {
            #8080端口號是開啟的本地服務(wù)端口號
            proxy_pass http://localhost:8080;
        }
        location /api  { 
            #每有一個新的代理需求注暗,就新增一個location
            #反向代理坛缕,達到前后端分離開發(fā)的目的
            proxy_pass http://192.168.60.225:7003;
        }
    }
}

/***********
關(guān)于proxy_pass

既是把請求代理到其他主機,其中 http://www.b.com/ 寫法和 http://www.b.com寫法的區(qū)別如下:
不帶/
location /html/
{
  proxy_pass http://b.com:8300;
}
帶/
location /html/
{
    proxy_pass http://b.com:8300/;
}

上面兩種配置捆昏,區(qū)別只在于proxy_pass轉(zhuǎn)發(fā)的路徑后是否帶 “/”赚楚。

  針對情況1,如果訪問url = http://server/html/test.jsp骗卜,則被nginx代理后宠页,請求路徑會便問http://proxy_pass/html/test.jsp,將test/ 作為根路徑膨俐,請求test/路徑下的資源勇皇。

  針對情況2罩句,如果訪問url = http://server/html/test.jsp焚刺,則被nginx代理后,請求路徑會變?yōu)?http://proxy_pass/test.jsp门烂,直接訪問server的根資源乳愉。

***********/

2、apache反向代理

用 apache 的 mod_proxy 模塊開啟反向代理功能來實現(xiàn):
1 修改 apache 配置文件 httpd.conf ,去掉以下兩行前面 # 號,加載反向代理模塊

    LoadModule proxy_module modules/mod_proxy.so
    LoadModule proxy_http_module modules/mod_proxy_http.so


2 在站點目錄中修改:
<VirtualHost *:80>
    DocumentRoot "E:\study-environment\www"
    #正向代理
    #ProxyRequests On
    #ProxyVia On
    #反向代理時設(shè)置為Off
    ProxyRequests Off  

    <Proxy "*">
      Order deny,allow
      #Deny from all  
      #Allow from 127.0.0.1 188.1.8.1
      Allow from all
    </Proxy>

    #proxy setting  
    ProxyPass           /api  http://188.1.8.1/api
    #ProxyPassReverse    瀏覽器的地址欄不會顯示反向代理的地址
    ProxyPassReverse    /api  http://188.1.8.1/api 
    #如果路徑的名稱/api代理后沒有變化屯远,session不會丟失蔓姚,則可以不用ProxyPassReverseCookiePath 
    #ProxyPassReverseCookiePath /api  /api  
      
    ProxyPass           /log http://188.1.8.1/apilog
    ProxyPassReverse    /log http://188.1.8.1/apilog
    ProxyPassReverseCookiePath /log /apilog
 
    #另一種寫法
    <Location / >
         ProxyPass        http://188.1.8.1/home connectiontimeout=5 timeout=300
         ProxyPassReverse http://188.1.8.1/home
     </Location>
</VirtualHost>
重啟 apache

//題外

正向代理(forward)是一個位于客戶端【用戶A】和原始服務(wù)器(origin server)【服務(wù)器B】之間的服務(wù)器【代理服務(wù)器Z】,為了從原始服務(wù)器取得內(nèi)容慨丐,用戶A向代理服務(wù)器Z發(fā)送一個請求并指定目標(服務(wù)器B)坡脐,然后代理服務(wù)器Z向服務(wù)器B轉(zhuǎn)交請求并將獲得的內(nèi)容返回給客戶端》拷遥客戶端必須要進行一些特別的設(shè)置才能使用正向代理备闲。

反向代理正好與正向代理相反晌端,對于客戶端而言代理服務(wù)器就像是原始服務(wù)器,并且客戶端不需要進行任何特別的設(shè)置恬砂∵志溃客戶端向反向代理的命名空間(name-space)中的內(nèi)容發(fā)送普通請求,接著反向代理將判斷向何處(原始服務(wù)器)轉(zhuǎn)交請求泻骤,并將獲得的內(nèi)容返回給客戶端漆羔。

簡單點說就是
正向代理:客戶向代理服務(wù)器發(fā)請求,代理服務(wù)器再把請求到指定服務(wù)器
反向代理:客戶向目標服務(wù)器發(fā)請求狱掂,代理服務(wù)器攔截請求演痒,再把請求轉(zhuǎn)發(fā)到指定的目標服務(wù)器

3、webpack+webpack-dev-server

//webpack.config.js
var path = require("path");

module.exports = {
    entry: {
        index: './src/index.entry.js',
        authManage: './src/authManage.entry.js',
        reports: './src/reports.entry.js'
    },
    output: {
        path: path.join(__dirname, 'public'),
        filename: '[name].bundle.js'
    },
    module: {
        loaders: [
            {
                test: /\.jsx?$/,
                loader: 'babel',
                exclude: /node_modules/,
                query: {
                    presets: ['es2015', 'react', "stage-2"]
                }
            },
            { test: /\.css$/, loader: 'style!css' },
            { test: /\.(png|jpg|jpeg|gif|woff)$/, loader: 'url-loader?limit=8192' }
        ]
    },
    devServer: {
        //代理
        proxy: {
            '/api/*': 'http://localhost:7003/'
            //'/api':  {
            //   target: 'http://localhost:7003/'
            //}
        }
  }
};


//在cmd敲入命令運行webpack-dev-server,例如
>>webpack-dev-server --inline --hot --progress --colors --port 8082
//也可以在package.json的scripts配置命令方便啟動服務(wù)或者配置new WebpackDevServer來啟動服務(wù)




//在頁面請求本地接口即可,devServer會攔截/api/的請求
//action.js
fetch(`/api/map?exhibition=${data._id}`)
      .then(res => res.json())
      .then(data => {
        let sourceId = data[0]._id;
        return fetch(`/api/exhibitor/${sourceId}`);
      })
      .then(res => res.json())
      .then(data => {
        return dispatch(addexhibition(data)) ;
      })
      .catch(error => {
        console.log(error);
      })

4趋惨、node+express+webpack+webpack-dev-middleware+http-proxy-middleware

//webpack-dev-server是一個小型的Node.js Express服務(wù)器,它使用webpack-dev-middleware來服務(wù)于webpack的包,除此自外嫡霞,它還有一個通過Sock.js來連接到服務(wù)器的微型運行時.所以我們也可以不用webpack-dev-server,自己來搭一個
//server.js
var path = require('path')
var express = require('express')
var webpack = require('webpack')
var proxyMiddleware = require('http-proxy-middleware')
var webpackConfig = require('./webpack.dev.conf')
var port = 7003
// 定義HTTP代理到自定義的API后端
var proxyTable = {
       '/api': {
        target: 'http://localhost:7003/',
        changeOrigin: true,
        // pathRewrite: {
        //   '^/api': '/api' 
        // }
      },
      '/Uploads': {
        target: 'http://localhost:7003/',
        changeOrigin: true
      }
    }

var app = express()
var compiler = webpack(webpackConfig)

var devMiddleware = require('webpack-dev-middleware')(compiler, {
  publicPath: webpackConfig.output.publicPath,
  stats: {
    colors: true,
    chunks: false
  }
})

var hotMiddleware = require('webpack-hot-middleware')(compiler)
// force page reload when html-webpack-plugin template changes
compiler.plugin('compilation', function (compilation) {
  compilation.plugin('html-webpack-plugin-after-emit', function (data, cb) {
    hotMiddleware.publish({ action: 'reload' })
    cb()
  })
})

// proxy api requests
Object.keys(proxyTable).forEach(function (context) {
  var options = proxyTable[context]
  if (typeof options === 'string') {
    options = { target: options }
  }
  app.use(proxyMiddleware(context, options))       //使用代理中間件
})

// handle fallback for HTML5 history API
app.use(require('connect-history-api-fallback')())

// serve webpack bundle output
app.use(devMiddleware)

// enable hot-reload and state-preserving
// compilation error display
app.use(hotMiddleware)

// serve pure static assets
var staticPath = path.posix.join(config.dev.assetsPublicPath, config.dev.assetsSubDirectory)
app.use(staticPath, express.static('./static'))

module.exports = app.listen(port, function (err) {
  if (err) {
    console.log(err)
    return
  }
  var uri = 'http://localhost:' + port
  console.log('Listening at ' + uri + '\n')

  // when env is testing, don't need open it
  if (process.env.NODE_ENV !== 'testing') {
    opn(uri)
  }
})


5、node+proxy-middleware

//主要針對采用https協(xié)議的服務(wù)器
var connect = require('connect');
var url = require('url');
var proxy = require('proxy-middleware');


var app = connect();
app.use('/api', proxy(url.parse('https://example.com/endpoint')));
// now requests to '/api/x/y/z' are proxied to 'https://example.com/endpoint/x/y/z'


//same as example above but also uses a short hand string only parameter
app.use('/api-string-only', proxy('https://example.com/endpoint'));

6希柿、node+cors

7诊沪、node+node-http-proxy

8、node+node-reverse-proxy

9曾撤、node+reverse-proxy

//通過pem和SNI 解決了 HTTPS 證書認證的問題

10端姚、http-proxy-middleware+broswerSync+gulp

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市挤悉,隨后出現(xiàn)的幾起案子渐裸,更是在濱河造成了極大的恐慌,老刑警劉巖装悲,帶你破解...
    沈念sama閱讀 212,718評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件昏鹃,死亡現(xiàn)場離奇詭異,居然都是意外死亡诀诊,警方通過查閱死者的電腦和手機洞渤,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,683評論 3 385
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來属瓣,“玉大人载迄,你說我怎么就攤上這事÷胀埽” “怎么了护昧?”我有些...
    開封第一講書人閱讀 158,207評論 0 348
  • 文/不壞的土叔 我叫張陵,是天一觀的道長粗截。 經(jīng)常有香客問我惋耙,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,755評論 1 284
  • 正文 為了忘掉前任绽榛,我火速辦了婚禮遥金,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘蒜田。我一直安慰自己稿械,他們只是感情好,可當我...
    茶點故事閱讀 65,862評論 6 386
  • 文/花漫 我一把揭開白布冲粤。 她就那樣靜靜地躺著美莫,像睡著了一般。 火紅的嫁衣襯著肌膚如雪梯捕。 梳的紋絲不亂的頭發(fā)上厢呵,一...
    開封第一講書人閱讀 50,050評論 1 291
  • 那天,我揣著相機與錄音傀顾,去河邊找鬼襟铭。 笑死,一個胖子當著我的面吹牛短曾,可吹牛的內(nèi)容都是我干的寒砖。 我是一名探鬼主播,決...
    沈念sama閱讀 39,136評論 3 410
  • 文/蒼蘭香墨 我猛地睜開眼嫉拐,長吁一口氣:“原來是場噩夢啊……” “哼哩都!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起婉徘,我...
    開封第一講書人閱讀 37,882評論 0 268
  • 序言:老撾萬榮一對情侶失蹤漠嵌,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后盖呼,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體儒鹿,經(jīng)...
    沈念sama閱讀 44,330評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,651評論 2 327
  • 正文 我和宋清朗相戀三年几晤,在試婚紗的時候發(fā)現(xiàn)自己被綠了约炎。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,789評論 1 341
  • 序言:一個原本活蹦亂跳的男人離奇死亡锌仅,死狀恐怖章钾,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情热芹,我是刑警寧澤,帶...
    沈念sama閱讀 34,477評論 4 333
  • 正文 年R本政府宣布惨撇,位于F島的核電站伊脓,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜报腔,卻給世界環(huán)境...
    茶點故事閱讀 40,135評論 3 317
  • 文/蒙蒙 一株搔、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧纯蛾,春花似錦纤房、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,864評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至碰煌,卻和暖如春舒岸,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背芦圾。 一陣腳步聲響...
    開封第一講書人閱讀 32,099評論 1 267
  • 我被黑心中介騙來泰國打工蛾派, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人个少。 一個月前我還...
    沈念sama閱讀 46,598評論 2 362
  • 正文 我出身青樓洪乍,卻偏偏與公主長得像,于是被迫代替她去往敵國和親夜焦。 傳聞我的和親對象是個殘疾皇子典尾,可洞房花燭夜當晚...
    茶點故事閱讀 43,697評論 2 351

推薦閱讀更多精彩內(nèi)容