前言:在以往開發(fā)中弱左,不會涉及到跨域的問題窄陡,因為往往都是在同一個項目中開發(fā)代碼或者單純寫小demo;However拆火,在實際項目中跳夭,前后端分成兩個不同的項目,各自部署在不同的域名下们镜,這也就會遇到跨域問題了币叹。
既然問題發(fā)生了,那就要從根本上去解決問題模狭,在開始說解決方案前颈抚,我們有必要了解一下什么是跨域
1、什么是跨域
在瀏覽器同源策略限制下嚼鹉,向不同源(不同協(xié)議贩汉、不同域名或者不同端口)發(fā)送XHR請求,瀏覽器認為該請求不受信任锚赤,禁止請求匹舞,具體表現(xiàn)為請求后不正常響應(yīng)
從定義中可以看到這都是瀏覽器做的“好事”。那么什么是不用源呢线脚?其實也很簡單赐稽,只要頁面的協(xié)議、域名和端口與請求地址的沒有完全一樣浑侥,就認為你是不同源姊舵,舉個極端例子來說:
http://www.baidu.com:80
與
https://msg.baidu.com:8080
2、怎么解決跨域
只要思想不滑坡寓落,辦法總比困難多蠢莺,既然已經(jīng)了解什么是跨域,那我們就著手來解決這個問題
1.從瀏覽器入手
其實跨域只在瀏覽器下才會觸發(fā)零如,那有沒有辦法禁用瀏覽器的同源策略呢躏将?辦法還真的有。
對于IE瀏覽器:進入ie的網(wǎng)際網(wǎng)路選項設(shè)置考蕾,然后選擇安全性祸憋,再選擇自訂等級,然后下拉肖卧,找到「存取跨網(wǎng)絡(luò)的資料來源」蚯窥,選擇啟用即可;
對于chrome瀏覽器:通過在命令行,輸入chrome.exe --allow-file-access-from-files --user-data-dir="C:/Chrome dev session" --disable-web-security拦赠,這會新建一個瀏覽器實例巍沙,自動打開的chrome會顯示一系列黃色的文字就說明成功了;
對于FireFox瀏覽器:在地址欄輸入about:config荷鼠,然后下拉找到security.fileuri.strict_origin_policy句携,然后設(shè)置為false即可
2.從域名入手
既然域名等信息不一致導(dǎo)致跨域產(chǎn)生,那么干脆就把兩個項目合并成一個項目允乐,使用相同的協(xié)議矮嫉、域名和端口。
3.從jsonp入手
其實認真想想牍疏,我們的系統(tǒng)中經(jīng)常會用到外鏈的圖片蠢笋、樣式文件以及插件,那這些不會導(dǎo)致跨域嗎鳞陨?是的昨寞,真的不會,因為這些是http請求厦滤,并不是前面定義的xhr(XmlHttpRequest)請求援岩。
既然圖片和js腳本可以正常請求,那么如果把script腳本的src改成我需要跨域請求的url是不是就可以了呢馁害?
是可以的,當(dāng)請求接口返回的數(shù)據(jù)需要稍微處理下蹂匹。在平常引入script腳本時碘菜,下載完文件后自動執(zhí)行,如果我們把src改成跨域url限寞,而且返回值是一段jjs語句忍啸,比如:
req.send("alert(1)"); //后臺返回js語句
那么前臺會會自動解析并執(zhí)行(這里是彈出1)。那么履植,如果返回的js語句是一個調(diào)用函數(shù)的js語句计雌,恰巧前臺事先定義了該函數(shù),如:
//前臺
function fun(msg){? alert(msg);? }
//后臺
req.send("fun('你渴望力量嗎')");
那么在fun函數(shù)將被調(diào)用(可以理解成后臺調(diào)用前臺代碼)玫霎,如果對fun的參數(shù)進行處理凿滤,就可以實現(xiàn)復(fù)雜的業(yè)務(wù)邏輯了。
在實際情況下是怎么處理的呢庶近?一般我們會先協(xié)商好需要被調(diào)用的方法及參數(shù)(fun),然后動態(tài)創(chuàng)建一個script標(biāo)簽翁脆,并設(shè)置該標(biāo)簽的src為跨域url,最后插入到文檔中鼻种,插入后反番,瀏覽器會自動發(fā)起http的get請求,下載完成后將會執(zhí)行后臺返回的js語句(后臺調(diào)用前臺)。
這就是jsonp罢缸。
4.從代理入手
還是回到最開始篙贸,既然瀏覽器認為非同源不安全,那么向同源請求不就行了枫疆?我們把請求轉(zhuǎn)到同一項目下的后臺爵川,在同項目的后臺進行xhr請求,然后把請求結(jié)果原樣返回給前臺养铸,這就是代理(或者叫轉(zhuǎn)發(fā))雁芙。
成功的原因是同源策略只在瀏覽器下才起作用,我用后臺來請求其他的url钞螟,那是不受影響的兔甘。開啟后臺代理可以用C#、JAVA鳞滨、NodeJs洞焙、甚至是python都行,只要能被前端訪問的并且能轉(zhuǎn)發(fā)請求就可以拯啦。
5.從CORS入手
除了前面的幾種方法外澡匪,還有一種簡單得多的方法,那就是后臺的請求響應(yīng)頭告訴瀏覽器“我的這個請求很安全褒链,允許當(dāng)前域名跨域訪問”唁情。如何去實現(xiàn)的呢,其實這是利用了CORS(Cross-Origin Resource Sharing, 跨源資源共享)甫匹,聽起來牛逼哄哄的甸鸟,其實也就是一個W3C新標(biāo)準,瀏覽器檢測到響應(yīng)頭的一些字段的值后兵迅,跳過同源策略抢韭。
那么有那些響應(yīng)頭字段,以及它們分別是什么定義呢恍箭?
res.header("Access-Control-Allow-Origin", "*"); //允許全部域名跨域刻恭,可以指定特點域名,逗號分隔
? res.header("Access-Control-Allow-Credentials", "true"); //允許攜帶cookie
? res.header("Access-Control-Allow-Headers", "X-Requested-With"); //允許傳輸?shù)恼埱箢^
? res.header("Access-Control-Allow-Methods", "PUT,POST,GET,DELETE,OPTIONS"); //允許發(fā)送的xhr模式
? res.header("X-Powered-By", " 3.2.1"); //快速模式
? res.header("Content-Type", "application/json;charset=utf-8"); //類型及字符編碼
3扯夭、如何實現(xiàn)跨域下的登陸
用前面任意一種方案都可以解決跨域鳍贾,然而,跨域帶來的登陸問題卻不好解決交洗。在說如何解決登陸問題前贾漏,還是按照老規(guī)則說一下問什么就登陸不了了。
在解析前需要先明確幾點:
1:http是無狀態(tài)的藕筋,不知道該請求歸屬于誰
2:每次http請求都會自動攜帶cookie(在請求頭中)
3:session存放在服務(wù)端纵散,是有時間有效性的梳码,一段時間內(nèi)不訪問將失效
在正常情況下(非跨域),我們前臺發(fā)出post請求伍掀,攜帶賬號掰茶、密碼、驗證碼蜜笤,提交給后臺濒蒋,后臺檢查數(shù)據(jù)后,設(shè)置session并返回登陸信息完成登陸把兔,這就是一個簡單的登錄流程沪伙。當(dāng)用戶發(fā)起一個新的請求的時候,后臺通過請求上下文可以獲取到已經(jīng)保存在服務(wù)端的session信息县好,如:
req.session["UserInfo"]?
//登錄時設(shè)置了key為UserInfo的session
乍一看就是這么一回事围橡,當(dāng)時仔細想想又會發(fā)現(xiàn)奇怪的地方:
①都說了http是無狀態(tài)的,在上一個請求設(shè)置的session缕贡,為什么在下一個請求中可以獲取到呢翁授?
②session的key是UserInfo,如果有多個用戶同時操作晾咪,拿到的值會不會是同一個呢收擦?
接下來我們先介紹一下session的設(shè)置與讀取
說起session,就不得不說cookie谍倦,兩者都是緩存塞赂,只不過前者保存在服務(wù)端,后者保存在客戶端昼蛀。在登錄完成后宴猾,后臺設(shè)置一個key為UserInfo的session對象(就叫對象S吧),這個對象S有三個重要的屬性曹洽,分別是key鳍置、value以及sid辽剧。key和value的非常好理解送淆,那sid是拿來干嘛?
其實sid又叫session_id怕轿,是這個session對象的主鍵偷崩。
眾所周知,http是無狀態(tài)的撞羽,為了區(qū)分狀態(tài)阐斜,在設(shè)置session的時候,服務(wù)端會自動在http請求的響應(yīng)頭中把sid設(shè)置到cookie中(開發(fā)人員感知不到這步操作的诀紊,也就是自動設(shè)置cookie)谒出,瀏覽器響應(yīng)請求后,會把更新帶有sid的cookie,并在下一個請求時自動帶上cookie(瀏覽器請求自動攜帶cookie笤喳,也是靜默操作为居,關(guān)于攜帶cookie的后面還會繼續(xù)介紹)。
當(dāng)?shù)诙握埱髞淼胶笈_杀狡,此時已經(jīng)帶上了sid的cookie蒙畴,如果這時候獲取session,服務(wù)器將會遍歷session列表呜象,匹配所有sid是cookies中的sid的session膳凝,再通過key作為索引找到相應(yīng)的結(jié)果。
這就是設(shè)置session和讀取session的流程恭陡,下面是一個簡單的圖片說明