最近做項目前後端分離才第一次遇到跨域問題,作為渣渣,當然是虛心學習網絡上一些前輩的經驗,以下是自己的總結。哪個地方有錯,還望告知,定虛心請教。
文章内容:
- 跨域原因
- 解決思路
- 解決方法
一、跨域原因
現在的web項目基本是前後端分離,前端調用後端接口,如果前後端代碼不屬于同一個域(同一域名同一端口号),就會産生跨域問題。
常見的跨域問題的報錯提示是:
Failed to load “被調用方域名”: No ‘Access-Control-Allow-Origin’ header is present on the requested resource. Origin ‘調用方域名’ is therefore not allowed access.
那麼,為什麼會發生AJAX跨域問題?
- 浏覽器限制
- 請求跨域通路不同域
- XHR(XMLHttpRequest)請求
當上述三個條件同時滿足時,就觸發了跨域問題啦~
二、解決思路
三、解決方法
1.去掉浏覽器限制(以下用chrome做示範)
這種方法并不具有可行性,你沒辦法讓浏覽器都這樣或者每個使用者自己實作。
啟動chrome時加
--disable-web-security
禁止它做跨域校驗。
2.使用JSONP解決
首先來了解一個JSOP是什麼?
JSONP是一個非官方協定,約定發送請求的參數中如果包含指定的參數,預設為callback.即JSONP請求,伺服器發現其指定參數時,就會把傳回值由原來的JSON對象改成JS代碼。
JSONP通過動态建立JS在JS中送出請求,用完就銷毀,js代碼的内容是函數調用的形式,它的函數名是callback的值,它的函數的參數是原先json對象。
其JS函數後面帶着時間戳防止被浏覽器緩存。此時Content-Type=application/javascript
使用JSONP時伺服器端代碼需要進行改動(這裡用Java代碼示例)
JSONP的弊端:
- 需要改動伺服器端
- 隻支援GET(因為是動态建立script,script隻能GET方法)
- 發的不是XHR請求(現在XHR有各種特性用于前端開發)
下面,我們來談談跨域問題主流的兩種解決方式吧
3.被調用方解決
- Filter解決方案
- nginx解決方案
- apache解決方案
- Spring架構解決方案
1.Filter解決方案
采用這種解決方案之前先來看看下面幾個問題。
浏覽器對于請求是先執行還是先判斷是否跨域?
先執行。
是不是所有請求都是先執行呢?
回答這個問題需要弄明白簡單請求和非簡單請求
浏覽器對于簡單請求,就是先執行後判斷。對于非簡單請求,它會先發一個OPTIONS預檢指令,檢查通過再執行。
那麼簡單請求和非簡單請求有哪些呢?
常見的【簡單請求】:GET、HEAD、POST
請求header裡面
無自定義頭
Content-Type為以下幾種:
text/plain
multipart/form-data
application/x-www-form-urlencoded
常見的【非簡單請求】:
PUT、DELETE的ajax請求
發送json格式的ajax請求
帶自定義頭的ajax請求
浏覽器如何判斷?
浏覽器發現請求是跨域的時候,它會在目前請求的請求頭中增加origins字段,然後等請求響應回來,
浏覽器會檢查響應頭中是否存在允許跨域的資訊,沒有就報錯。
帶Cookie的跨域——Access-Control-Allow-Origin: *是否滿足所有跨域場景?
請看下圖:
答案顯然是否,對于帶Cookie的跨域請求必須明确指定Access-Control-Allow-Origin的域名,
并設定Access-Control-Allow-Credentials為true。
最後,來看下Filter解決方案的示例代碼吧。
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
HttpServletResponse res = (HttpServletResponse) response;
HttpServletRequest req = (HttpServletRequest) request;
String origin = req.getHeader("Origin");
if (!org.springframework.util.StringUtils.isEmpty(origin)) {
//帶cookie的時候,origin必須是全比對,不能使用*
res.addHeader("Access-Control-Allow-Origin", origin);
}
// 允許所有請求方法
res.addHeader("Access-Control-Allow-Methods", "*");
String headers = req.getHeader("Access-Control-Request-Headers");
// 支援所有自定義頭
if (!org.springframework.util.StringUtils.isEmpty(headers)) {
res.addHeader("Access-Control-Allow-Headers", headers);
}
res.addHeader("Access-Control-Max-Age", "3600");
// enable cookie
res.addHeader("Access-Control-Allow-Credentials", "true");
chain.doFilter(request, response);
}
2.nginx解決方案
3.apache解決方案
4.Spring架構解決方案
controller類上加@CrossOrigin注解
4.調用方解決——隐藏跨域
調用方解決跨域問題隻有一種方式,就是利用Http伺服器進行隐藏跨域啦,下面給出nginx和apache兩個伺服器隐藏跨域的配置方式
1.反向代理——nginx配置
調用方設定nginx反向代理
2.反向代理——Apache配置
調用方設定Apache反向代理