1. 程式人生 > >熟悉又陌生的跨域訪問與CORS

熟悉又陌生的跨域訪問與CORS

說到跨域訪問,是既熟悉又陌生,熟悉是因為只要做過web專案,尤其是前後端分離的專案,都碰到過ajax跨域訪問的麻煩,跨域訪問就如字面意思,只要協議、域名、埠有任何一個不同,都被當作是不同的域。對於跨域訪問,是有限制的;陌生的是,很多跨域無法訪問的問題只能一味網上找解決方案,而不知道跨域乃至CORS的原理,配置出了問題不知道如何下手, 所以這次記錄一下徹底解決下跨域的問題。

為什麼要有跨域限制

拒絕其它域名的訪問想想理由也很簡單,如果沒有跨域訪問限制,別人拿你的cookie偽造你的身份,任何人都能偽造請求,安全性會出現問題。

CORS跨域資源共享

如果要放開限制,可以採用CORS跨域資源共享,CORS 基本思想就是使用自定義的HTTP頭部讓瀏覽器與伺服器進行溝通,從而決定請求或響應是應該成功還是失敗。JSONP方案我沒用過,聽說有限制,不如CORS。

CORS 在後端的設定主要在於檢查 請求頭,檢查的內容包括但不限於

  • Origin:請求源:傳送請求的伺服器域名
  • Methods:請求方式:如POST,GET
  • Headers:可以攜帶的請求頭資訊:包括 “Content-Type”,“Origin“,”Accept“ 等
  • Credentials:可否攜帶cookies

具體的檢查程式碼:

@Component
public class CorsFilter implements Filter {

    final static org.slf4j.Logger logger = org.slf4j.LoggerFactory.getLogger(CorsFilter.class);

    @Override
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException { HttpServletResponse response = (HttpServletResponse) res; HttpServletRequest reqs = (HttpServletRequest) req; //檢查Origin是否符合,因為這裡直接取的 請求頭的Origin地址,所以相當於不限制請求源是哪裡
response.setHeader("Access-Control-Allow-Origin", reqs.getHeader("Origin")); //這裡設定伺服器允許帶cookie response.setHeader("Access-Control-Allow-Credentials", "true"); //這裡設定伺服器允許POST, GET, OPTIONS, DELETE四種請求Method來請求 response.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE"); //這裡設定伺服器允許額外攜帶的請求頭Origin, header1, header2, header3, X-Requested-With, Content-Type, Accept等欄位 response.setHeader("Access-Control-Allow-Headers", "Origin, header1, header2, header3, X-Requested-With, Content-Type, Accept"); chain.doFilter(req, res); } @Override public void init(FilterConfig filterConfig) { } @Override public void destroy() { } }

CORS跨域訪問分兩種情況:

第一種情況

:如果Content-Type是application/x-www-form-urlencoded, multipart/form-data, 或者 text/plain,或者請求方法不是PUT或DELETE,則是普通情況處理
下圖是普通情況跨域的請求和返回資訊:
這裡寫圖片描述

傳送的Request Header除了常見的,額外的就是”Origin“欄位;後臺也會返回Access-Control-Allow-Origin的設定值,瀏覽器發現Origin欄位不在Access-Control-Allow-Origin的設定值範圍內,那就會報錯,跨域訪問就會失敗。

第二種情況

前面說到,如果Content-Type是application/x-www-form-urlencoded, multipart/form-data, 或者 text/plain,或者請求方法不是PUT或DELETE,則是普通情況處理;
那相反的,如果Content-Type不是application/x-www-form-urlencoded, multipart/form-data, 或者 text/plain,比如application/json,或者請求方法是PUT或DELETE,就會在正式訪問前增加一次HTTP OPTIONS查詢請求,大白話就是瀏覽器會先詢問伺服器,當前網頁所在的域名是否在伺服器的許可名單之中,以及可以使用哪些HTTP Method和頭資訊欄位(Headers)。只有得到肯定答覆,瀏覽器才會發出正式的請求,否則就報錯。

jquery的ajax文件中也有類似描述

這裡寫圖片描述

那我們再看下第二種情況下 跨域的請求和返回資訊:
這裡寫圖片描述

截圖的是OPTIONS請求,後面正式的請求跟普通情況下的無異,就不討論。OPTIONS請求你會發現多了兩個Access-Control-Request-Methods和Access-Control-Request-Headers。這兩個內容跟後臺設定的Access-Control-Allow-Methods和Access-Control-Allow-Headers匹配

圖中返回資訊的Allow就是伺服器返回的支援的方法。特別也要注意Access-Control-Allow-Headers的設定,在之前的一個專案中,我就是因為後臺在設定Access-Control-Allow-Headers的時候,沒有加上”Content-Type“,導致當前臺傳的Access-Control-Request-Headers帶有”Content-Type“的時候,遊覽器會因為匹配不上而報錯。

結語

以上就是跨域和CORS流程的梳理,知己知彼,百戰百勝,只有瞭解機制才能方便我們在跨域訪問時早點定位問題所在。在這裡再推薦一篇文章,對於跨域和CORS有更細緻的講解,特分享給大家:

http://www.ruanyifeng.com/blog/2016/04/cors.html