-
為什么發(fā)生Ajax跨域?
1、瀏覽器顯示?
2、訪問的路徑不是本地的
3、發(fā)送的請求是XMLHttprequest的請求
查看全部 -
csrf filter
查看全部 -
為什么會發(fā)生ajax跨域/ 跨域的場景
總的來說, 產(chǎn)生跨域問題有3個原因: ?瀏覽器的限制, 跨域, xhr(XMLHttpRequest) 請求; 這三個條件同時滿足才會產(chǎn)生跨域安全問題
瀏覽器的限制
一句話解釋就是: 因為發(fā)送請求時跨域了所以瀏覽器報跨域問題
跨域不成功和后臺服務(wù)器沒有關(guān)系, 不是因為后臺不允許前臺調(diào)用, 真正的原因是瀏覽器出于安全考慮, 會對發(fā)送的不同源跨域請求進行安全校驗,校驗不通過時就會報跨域錯誤。
這個時候可能你的請求已經(jīng)發(fā)送成功并且返回了數(shù)據(jù), 但控制臺報了一條跨域錯誤, 說明服務(wù)器后臺式?jīng)]有任何限制的, ?前臺已經(jīng)訪問到了后臺, ?是瀏覽器報的錯; 說白了就是瀏覽器多管閑事, 而不是后臺不允許
跨域: 發(fā)出去的請求不是本域的. ?前臺發(fā)送的請求里面和后臺的 協(xié)議, 域名, 端口任何一個不一樣, 瀏覽器都會認(rèn)為是跨域; 比如前臺是localhost:8080端口, 請求的是localhost:8081端口 , 雖然域名相同, 但端口不一樣, 所以跨域了;
發(fā)送的是 XHR(XMLHttpRequest) 請求 (也是最重要的原因); 只要發(fā)送的不是XHR請求, 比如發(fā)送的請求Type為jsonp, 就算是跨域了, 瀏覽器也不會報跨域安全問題
跨域解決方案
命令行啟動參數(shù)修改
Cors, 在響應(yīng)頭里面添加字段
nodejs可以引入第三方包cors
nginx反向代理實現(xiàn)跨域(proxy_pass模塊)
Vue.config.js里面配置devServer的proxy
谷歌的插件allow cors可以解決谷歌瀏覽器里面的跨域
webSocket本身不存在跨域的問題,所以我們可以利用webSocket來進行非同源中間的通信
從瀏覽器出發(fā), 讓瀏覽器不去做跨域的限制;
方法1: 瀏覽器禁止跨域安全檢查
思路: 通過命令行修改瀏覽器啟動參數(shù), 讓瀏覽器不進行跨域安全校驗, 從而允許跨域;
做法: 命令行啟動瀏覽器后添加參數(shù) ?- -disable-web-security; 如 chrome --disable-web-security, 這個參數(shù)的作用是禁止瀏覽器進行跨域安全檢查;
缺陷:
?總結(jié): 雖然可以解決跨域, 但是這種方式價值不大, 因為需要用戶手動修改, 而且這種改動是客戶端的改動, 不太實用, 而在實際項目中, 主要是對服務(wù)端進行修改來實現(xiàn)瀏覽器支持跨域;
每次都需要通過命令行啟動參數(shù)來啟動瀏覽器, 過于繁瑣
該方法會導(dǎo)致穩(wěn)定性和安全性有所下降
該方法是客戶端方法的改動, 在實際使用中, 要每個客戶端都禁止瀏覽器跨域檢查是不太現(xiàn)實的, 實用性較低
JSONP (json with padding; jsonp 是json的一種補充使用) 避免發(fā)生跨域, ?因為只有xhr請求才會可能發(fā)生跨域, ?而jsonp 的請求類型不是xhr, 而且主要是通過動態(tài)創(chuàng)建script腳本, 利用script標(biāo)簽向不同源接口請求資源可以跨域的特點, 在script里面發(fā)送跨域請求, 獲取其他來源動態(tài)生成的json數(shù)據(jù), ?所以不會產(chǎn)生跨域問題;
jsonp 與ajax 請求的區(qū)別
思路: JSONP是一個非官方協(xié)議, 是一個約定, 它約定如果發(fā)送請求的時候在script標(biāo)簽里攜帶一個 callback 參數(shù), callback 的值是一個頁面中定義好的函數(shù)名, ?后臺檢測到 callback 就知道這是一個jsonp請求, 這時服務(wù)器返回的數(shù)據(jù)就會由原來的json對象改為js代碼;
函數(shù)是瀏覽器定義的, 處理邏輯瀏覽器寫; ?數(shù)據(jù)是 服務(wù)器返回的, ?到底是什么數(shù)據(jù) 服務(wù)器決定;
實現(xiàn)原理: 前臺使用 ajax 的 get 請求, 將 dataType 設(shè)為 'jsonp' , 服務(wù)器創(chuàng)建一個類并繼承抽象類 ?AbstractJsonResponseBodyAdvice, 最后注解 @ControllerAdvice;
具體實現(xiàn)流程:
補充: jsonp請求里除了callback參數(shù)外, 還一個下劃線 _ ? 參數(shù),參數(shù)值是一串隨機數(shù)字, 它的作用主要是防止請求被緩存, 如果需要緩存的話, ?可以在ajax請求里面加上 cache: true, 表示結(jié)果可以被緩存;
JSONP優(yōu)缺點
JSONP優(yōu)點是簡單兼容性好,可用于解決主流瀏覽器的跨域數(shù)據(jù)訪問的問題。
缺點:
不安全,可能會遭受XSS攻擊;
發(fā)送的不是xhr請求; xhr的一些事件, 異步功能jsonp都沒有;
僅支持get方法, 不能滿足實際開發(fā)需求; jsonp是通過動態(tài)創(chuàng)建script發(fā)請求, script 只支持get
服務(wù)器需要改動代碼來支持跨域, 當(dāng)接口不是我們自己的代碼無法改動時, jsonp就無能為力了;
聲明一個回調(diào)函數(shù),其函數(shù)名(如show)當(dāng)做參數(shù)值,要傳遞給跨域請求數(shù)據(jù)的服務(wù)器,函數(shù)形參為服務(wù)器返回的data。
創(chuàng)建一個
<script>
標(biāo)簽,把跨域的API接口地址賦值給script的src 并且在這個地址中攜帶這個show 函數(shù)名(通過問號傳參:?callback=show)。服務(wù)器接收到請求后,把傳遞進來的函數(shù)名和它需要給你的數(shù)據(jù)拼接成一個字符串,例如:傳遞進去的函數(shù)名是show,它準(zhǔn)備好的數(shù)據(jù)是
show('你好么')
。服務(wù)器把數(shù)據(jù)返回給瀏覽器, ?返回結(jié)果為js代碼, js代碼的內(nèi)容是函數(shù)調(diào)用的形式, ?函數(shù)名為callback 的值, 函數(shù)的參數(shù)為之前要返回的json對象, 瀏覽器就可以執(zhí)行之前聲明 的這個回調(diào)函數(shù), 對返回的數(shù)據(jù)進行操作;
相當(dāng)于向
http://localhost:3000/say?wd=Iloveyou&callback=show
這個地址請求數(shù)據(jù),然后后臺返回show('你好么')
,最后會運行show()這個函數(shù),打印出'你好么'
ajax屬于同源策略, ?jsonp屬于非同源策略; ?
ajax的核心是通過XmlHttpRequest獲取非本頁內(nèi)容,而jsonp的核心則是動態(tài)添加<script>標(biāo)簽來調(diào)用服務(wù)器提供的js腳本。
普通的ajax請求發(fā)出去的類型是 xhr, jsonp 請求發(fā)出去的類型是script, ajax請求返回的數(shù)據(jù)類型是json, ?jsonp返回類型是 js腳本; ?
產(chǎn)生跨域后解決
解決思路:
方式:
一. 從被調(diào)用方考慮,有三種情況,分別是服務(wù)器實現(xiàn) (重點),nginx配置和apache配置。
原理: 在這種解決方案里面, 跨域請求是直接從調(diào)用方的瀏覽器發(fā)送到被調(diào)用方的http服務(wù)器, http服務(wù)器再把請求轉(zhuǎn)發(fā)到應(yīng)用服務(wù)器; 應(yīng)用服務(wù)器處理完之后, 把響應(yīng)返回給http服務(wù)器, http服務(wù)器再把響應(yīng)轉(zhuǎn)回到調(diào)用方的瀏覽器;
最終的目標(biāo)是要在響應(yīng)頭里添加字段, 所以這里有兩個地方可以增加響應(yīng)頭; 一個是中間的http服務(wù)器上增加, 也就是Nginx/ Apache; 一個是應(yīng)用服務(wù)器, 也就是Tomcat
被調(diào)用方; 基于讓被調(diào)用方支持跨域的思路 , 支持基于http協(xié)議關(guān)于跨域方面的要求; ?也就是從A域名調(diào)用b域名的時候, 在b域名返回的響應(yīng)頭里添加指定字段, 告訴瀏覽器, 我允許A域名調(diào)用, 瀏覽器通過校驗, 就不會報跨域錯誤;
調(diào)用方; 如果被調(diào)用方不是公司的, 無法進行修改, 就可以從調(diào)用方進行修改, 這種做法是隱藏跨域, 通過一個代理, 從瀏覽器里發(fā)出的都是A域名的請求, 通過這個代理把指定的url轉(zhuǎn)到B域名里, 在瀏覽器看來它們就是同一個域名, 就沒有跨域問題
被調(diào)用方解決: CORS
CORS 需要瀏覽器和后端同時支持。IE 8 和 9 需要通過 XDomainRequest 來實現(xiàn)。
瀏覽器會自動進行 CORS 通信,實現(xiàn) CORS 通信的關(guān)鍵是后端;
服務(wù)端設(shè)置 Access-Control-Allow-Origin 就可以開啟 CORS;
瀏覽器在發(fā)送跨域請求時, 會先判斷請求是簡單請求和非簡單請求。 簡單請求是先執(zhí)行請求再驗證,非簡單請求是先驗證再請求。 非簡單請求實際上會發(fā)送兩個請求, 第一次會先發(fā)送一個預(yù)檢請求, 這個預(yù)檢請求是瀏覽器額外發(fā)送的 options 預(yù)檢請求, ?檢查通過之后, 才會真正把跨域請求發(fā)送出去;
預(yù)檢命令的緩存: 非簡單請求中添加的響應(yīng)頭, 用于緩存預(yù)檢命令, 提高請求效率; 告訴瀏覽器在一個小時之內(nèi)可以緩存預(yù)檢命令的結(jié)果, 發(fā)送非簡單請求時不用再發(fā)送預(yù)檢命令 ?res.setHeader('Access-Control-Max-Age', 3600)
簡單請求(比較常見):
方法為 get,head,post 中的一種;
請求header里面沒有自定義頭;
Content-Type的值為限于以下3者之一: ?text/plain, multipart/form-data, application/x-www-form-urlencoded
非簡單請求(比較常見):
put, delect方法的ajax請求;
發(fā)送json格式的ajax請求;
帶自定義頭的ajax請求。
簡單請求處理方案:在響應(yīng)頭中添加 ? ? ?
**Access-Control-Allow-Origin=“允許跨域的url”**,即跨省域時,請求頭Origin的值,所以一般是獲取Origin的值。 **Access-Control-Allow-Method=“*”**,允許的方法。
非簡單請求處理方案:響應(yīng)頭中添加
"*"?表示允許所有內(nèi)容或類型;? Access-Control-Allow-Origin=“允許跨域的url”,即跨域時,可以獲取請求頭Origin的值。 Access-Control-Allow-Method=“*”,//?允許所有方法 'Access-Control-Allow-Origin',?*?//?允許所有域????? res.setHeader('Access-Control-Request-Headers',?'Content-Type') Access-Control-Request-Headers=“Content-Type"?//?自定義的header的key? 如:? ?//?設(shè)置哪個域/?源可以跨域調(diào)用 ????res.setHeader('Access-Control-Allow-Origin',?origin) ????//?允許攜帶哪個頭?可以跨域 ????res.setHeader('Access-Control-Allow-Headers',?'name') ????//?允許哪個方法?可以跨域 ????res.setHeader('Access-Control-Allow-Methods',?'PUT') ????//?允許攜帶cookie ????res.setHeader('Access-Control-Allow-Credentials',?true) ???? //?預(yù)檢命令的緩存時間;?用于緩存預(yù)檢命令,?提高請求效率;?告訴瀏覽器在一個小時之內(nèi)可以緩存這段信息,?發(fā)送非簡單請求時不用再發(fā)送預(yù)檢命令 ????res.setHeader('Access-Control-Max-Age',?3600) ????//?允許返回的頭 ????res.setHeader('Access-Control-Expose-Headers',?'name')
帶cookies的跨域解決:在響應(yīng)頭添加
1) 帶cookie的時候, origin 必須是全匹配, 不能使用*, 否則會報錯, 并且要添加 Access-Control-Allow-Credentials="true" 的字段;
res.setHeader('Access-Control-Allow-Origin',?"http://locahost:?8081") Access-Control-Allow-Credentials="true"??//??允許發(fā)送請求時攜帶cookies
http里的會話, 也就是session, 是依賴于cookie的, sessionid存放在cookie中, 在實際工作中, 帶cookie的跨域很重要;
2) 用 filter實現(xiàn)解決: 瀏覽器發(fā)現(xiàn)請求是跨域的時候, 會在請求頭里添加origin字段, 這個字段有當(dāng)前域的信息, 后臺可以在filter里得到這個字段, 然后再把它寫回到 Access-Control-Allow-Origin 里面, 這樣就可以支持所有域的調(diào)用;
帶自定義頭的跨域
在jQuery里面, 設(shè)置自定義頭有兩種方法:
1) 在headers里添加;
2) XMLHttpRequest 對象提供了一個設(shè)置請求頭的方法: setRequestHeader, 可以在 beforeSend 回調(diào)函數(shù)里用xhr.setRequestHeader("x-header2":, "aaa") 方法設(shè)置;
請求頭里的自定義屬性:
獲取請求頭的自定義屬性 , 然后再把它寫到響應(yīng)頭的 Access-Control-Allow-Origin 里面 , 就可以支持所有自定義頭
nodejs可以引入第三方包cors
npm?install?--save?cors
//CORS?middleware var?allowCrossDomain?=?function(req,?res,?next)?{ ????res.header('Access-Control-Allow-Origin',?'http://example.com'); ????res.header('Access-Control-Allow-Methods',?'GET,PUT,POST,DELETE'); ????res.header('Access-Control-Allow-Headers',?'Content-Type'); ????next(); } //... app.configure(function()?{ ????app.use(express.bodyParser()); ????app.use(express.cookieParser()); ????app.use(express.session({?secret:?'cool?beans'?})); ????app.use(express.methodOverride()); ????app.use(allowCrossDomain); ????app.use(app.router); ????app.use(express.static(__dirname?+?'/public')); });
被調(diào)用方解決: ngnix反向代理配置 實現(xiàn)跨域(proxy_pass模塊):
虛擬主機: 多個域名指向同一個服務(wù)器, 服務(wù)器根據(jù)不同的域名把請求轉(zhuǎn)到不同的應(yīng)用服務(wù)器, 看上去好像有多個主機, 實際上只有一個主機;
server{ ??#?監(jiān)聽9099端口 ??listen?9099; ??#?域名是localhost ??server_name?localhost; ??#凡是localhost:9099/api這個樣子的,都轉(zhuǎn)發(fā)到真正的服務(wù)端地址http://localhost:9871 ??location?^~?/api?{ ??????proxy_pass?http://localhost:9871; ??} }
被調(diào)用方- Apache解決
和 Nginx作用是一樣的, ?做法就是把Nginx上的配置再實現(xiàn)一次;
Vue中 webpack.config.js里面配置devServer的proxy
利用node + webpack + webpack-dev-server代理接口跨域。在開發(fā)環(huán)境下,由于vue渲染服務(wù)和接口代理服務(wù)都是webpack-dev-server同一個,所以頁面與代理接口之間不再跨域,無須設(shè)置headers跨域信息了。后臺可以不做任何處理。
module.exports?=?{ ??entry:?{}, ??module:?{}, ??... ??devServer:?{ ??????historyApiFallback:?true, ??????proxy:?[{ ??????????context:?'/login', ??????????target:?'http://www.daxihong.com:8080',??//?代理跨域目標(biāo)接口 ??????????changeOrigin:?true, ??????????secure:?false,??//?當(dāng)代理某些https服務(wù)報錯時用 ??????????cookieDomainRewrite:?'www.daxihong.com'??//?可以為false,表示不修改 ??????}], ??????noInfo:?true ??} }
谷歌的插件allow cors可以解決谷歌瀏覽器里面的跨域
webSocket本身不存在跨域的問題, ?所以我們可以利用webSocket來進行非同源中間的通信
查看全部 -
經(jīng)過創(chuàng)建的 過濾器的過濾操作后,filter 解決 了 瀏覽器對跨域請求的攔截,當(dāng)有多個地址需要進行相同操作時,可以使用"*"代替具體的地址,從而實現(xiàn)大范圍的跨域請求
查看全部 -
解決跨域問題①:
針對該瀏覽器添加參數(shù),禁止瀏覽器做校驗,避免報跨域問題
查看全部 -
修改瀏覽器的設(shè)置后,沒有再出現(xiàn)跨域問題
由此可以認(rèn)為,跨域問題主要還是在瀏覽器發(fā)生的校驗失敗等問題導(dǎo)致的,與后臺沒有任何關(guān)系
查看全部 -
發(fā)生Ajax 跨域請求的原因
查看全部 -
Ajax 跨域請求 限制原因:
3、XHR(XMLHttpRequest)請求:
測試: 新增加一個 <img>標(biāo)簽,重啟啟動后,由圖可知,盡管新添加了一個 <img>標(biāo)簽,但是,瀏覽器所報的錯誤還是只有一個,而這一個便是之前的<a>標(biāo)簽的請求;而這是由于 <img> 和 <a> 兩個請求發(fā)送時的 Type 不同,<img> 是json類型的,而<a>則是xhr類型的
查看全部 -
Ajax 跨域請求 限制原因:
2、跨域:
由圖中可知,server? 和 client 的 port 端口是不同的,因此,瀏覽器檢測到后認(rèn)為其發(fā)生了跨域
查看全部 -
Ajax 跨域請求 限制原因:
1、瀏覽器限制:
由圖中可以知道,當(dāng)發(fā)送請求時,盡管控制臺報了跨域請求錯誤,但是IDE的服務(wù)器后臺,依舊正常收到了請求,并打印相關(guān) 響應(yīng)字段
查看全部 -
Ajax 跨域請求 限制原因:
1、瀏覽器限制:
由截圖可知,服務(wù)器的請求響應(yīng)狀態(tài)為200,這代表這客戶端的請求被服務(wù)器正常的響應(yīng)了,服務(wù)器后臺是沒有任何限制,而且正確處理 了;從側(cè)面也就證明了是 瀏覽器進行了限制,從而報了錯誤給我們
查看全部 -
引入jasmine 測試框架后,console 會生成一個界面用于提示用戶測試結(jié)果及相關(guān)錯誤
查看全部 -
引入前端測試框架 jasmine,提高開發(fā)效率,從官網(wǎng)進入到 Releases目錄下下載 jasmine .zip 壓縮文件,解壓后將lib目錄下的包復(fù)制到工程項目的static目錄下
查看全部 -
引入 jasmine 測試框架
查看全部 -
創(chuàng)建 ajaxClient 工程并編寫 前臺頁面代碼
查看全部
舉報