日韩无码专区无码一级三级片|91人人爱网站中日韩无码电影|厨房大战丰满熟妇|AV高清无码在线免费观看|另类AV日韩少妇熟女|中文日本大黄一级黄色片|色情在线视频免费|亚洲成人特黄a片|黄片wwwav色图欧美|欧亚乱色一区二区三区

RELATEED CONSULTING
相關咨詢
選擇下列產(chǎn)品馬上在線溝通
服務時間:8:30-17:00
你可能遇到了下面的問題
關閉右側工具欄

新聞中心

這里有您想知道的互聯(lián)網(wǎng)營銷解決方案
Cors跨域(四):解決方案對決JSONP vs CORS

前言

你好,我是YourBatman。

創(chuàng)新互聯(lián)建站擁有一支富有激情的企業(yè)網(wǎng)站制作團隊,在互聯(lián)網(wǎng)網(wǎng)站建設行業(yè)深耕10多年,專業(yè)且經(jīng)驗豐富。10多年網(wǎng)站優(yōu)化營銷經(jīng)驗,我們已為上1000家中小企業(yè)提供了成都網(wǎng)站建設、網(wǎng)站設計解決方案,按需求定制網(wǎng)站,設計滿意,售后服務無憂。所有客戶皆提供一年免費網(wǎng)站維護!

挖掘機技術哪家強,山東技校找藍翔;跨域問題怎么解,CORS還是JSONP?

關于瀏覽器跨域問題的解決方案,坊間一直“傳聞”著兩種解決方案:JSONP和CORS。由于文章的歷史背景不同,作者偏好不一樣,搞得好些同學迷惑得很,去谷歌里百度搜尋答案時經(jīng)常就是這種趕腳。

作為一家負責任的“技校”(負責人的技術專欄),今天通過此文徹底給你解釋清楚并給出確定的答案,助你快速選擇正確的道路解決問題。

所屬專欄

  • 點撥-Cors跨域

本文提綱

版本約定

  • JDK:8
  • Servlet:4.x
  • tomcat:9.x

正文

同源策略是瀏覽器最核心也最基本的安全功能。當被瀏覽器半信半疑的腳本運行在沙箱時,它們應該只被允許訪問來自同一站點的資源,而不是那些來自其它站點可能懷有惡意的資源。但是呢,在現(xiàn)在的互聯(lián)網(wǎng)場景中,跨域訪問是一種必須,所以才有了解決跨域問題的方案。

兩大方案:JSONP和CORS

對于跨域共享資源,一共有兩大解決方案

  • JSONP:老一代瀏覽器解決方案
  • CORS:全新一套標準的解決方案

JSONP方案

和iPhone 7和iPhone 7P不一樣,JSONP 不等于 JSON Plus,全稱是JSON with Padding。JSON是一種基于文本的數(shù)據(jù)交換格式,而JSONP是一種使用模式,可以讓網(wǎng)頁從別的域訪問資源,從而完成跨域資源共享。

本系列第一篇文章就說到: 

  •  
  •  
  •  
  •  
  •  
  •  
  •  
  • 說明:利用script的src發(fā)送http請求到服務端,因此此script標簽務必放在function的下面(因為瀏覽器是從上至下渲染)

    服務端代碼:托管在8080端口

     
     
     
     
    1. /** 
    2.  * 在此處添加備注信息 
    3.  * 
    4.  * @author YourBatman. Send email to me 
    5.  * @site https://yourbatman.cn 
    6.  * @date 2021/6/9 10:36 
    7.  * @since 0.0.1 
    8.  */ 
    9. @Slf4j 
    10. @WebServlet(urlPatterns = "/jsonp") 
    11. public class JSONPServlet extends HttpServlet { 
    12.  
    13.     @Override 
    14.     protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { 
    15.         String callback = req.getParameter("callback"); 
    16.         resp.getWriter().write(callback + "('hello jsonp...')"); 
    17.     } 

    說明:可以看到服務端的代碼非常的清爽,不涉及到任何請求頭/響應頭

    打開頁面,發(fā)送JSONP請求,結果如下:

    請求的響應體:

    瀏覽器控制臺輸出:

    完美。通過JSONP我們實現(xiàn)了訪問不同域的資源,實現(xiàn)了跨域。

    用jQuery的ajax發(fā)送異步JSONP請求

    上例是使用 

  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  • 通過jQuery大大改善了js代碼的書寫方式,使得結構更加優(yōu)雅、直觀。這就是jQuery最厲害的語法糖能力~

    說明:JsonP only works with type: GET。也就說type即使你寫成post,jQuery也會給你轉成get

    服務端不變,發(fā)送異步請求,結果如下:

    關注點:

    1. Ajax的callback回調函數(shù)名是動態(tài)生成的,并且確保了唯一性
    2. 由于服務端并不關心回調的函數(shù)名名稱,因此回調函數(shù)名的長短沒有關系(瀏覽器自己能識別就成)

    影響體如下:

    瀏覽器控制臺打?。?/p>

    完美。對于有技術敏感性的你來講,應該能發(fā)現(xiàn)底層原理依舊還是script的src,只是寫法不一樣,僅此而已。

    優(yōu)缺點

    JSONP跨域方案作為一種“古老”方式,有如下優(yōu)缺點:優(yōu)點:

    • 對老瀏覽器(如IE8、7等)有非常好的兼容性
    • 書寫起來比較簡單,容易理解(畢竟沒有那么多的請求頭、響應頭需要考慮嘛)

    缺點:

    • 只能發(fā)送get請求,不支持POST、PUT等請求方式,這是硬傷
    • 安全度不高。因為JSONP是利用函數(shù)回調來由瀏覽器執(zhí)行目標函數(shù),這樣宿主web其實是比較容易受到各類攻擊的

    總的來講,隨著Cors規(guī)范在2014年的正式確定,現(xiàn)代的瀏覽器100%支持Cors規(guī)范。由于瀏覽器的更新?lián)Q代,JSONP的最大優(yōu)勢(兼容老瀏覽器)也就不復存在了,所以在實際開發(fā)中的使用建議是:不要使用JSONP,而應擁抱CORS。

    CORS方案

    由于JSONP方案存在一些不足(比如只支持Get請求就是硬傷),并不能很好的滿足對跨域資源共享的需求,因此就出現(xiàn)了當下主流的跨域規(guī)范:CORS(Cross-origin resource sharing)

    不同于JSONP的方案,CORS方案更強大實用,但稍微復雜那么一丟丟。其背后的基本思想是:使用自定義的HTTP頭部和瀏覽器“溝通”,讓瀏覽器和服務器相互“了解”對方,從而決定請求或響應成功與否。

    說明:CORS 并不是為了解決服務端安全問題而出現(xiàn),而是為了解決如何跨域調用資源。至于如何設計出安全的、開放的API,這就是安全范疇了(如可加上token驗證、請求有效期、ip來源驗證等手段)

    CORS的WD(工作草案)從2009-03-17開始,2014-01-16進入REC(推薦標準)階段,可謂正式畢業(yè)。起初CORS的推廣的主要障礙是當時市面上的老瀏覽器并不支持它(比如當時市場占有率極大的IE 6、7、8這種老家伙),畢竟這個規(guī)范是新的只有升級的新瀏覽器才會支持到。

    但歷史的巨輪永遠是滾滾向前,現(xiàn)在已經(jīng)2021年了,現(xiàn)今市面上的瀏覽器對CORS規(guī)范的支持情況如下圖所示(數(shù)據(jù)來源于:http://caniuse.com):

    看到這張圖,應該可以毫不客氣的說:所有的瀏覽器(包括手機、PAD等瀏覽器)均已支持CORS規(guī)范

    版本上拿Chrome瀏覽器舉例:我現(xiàn)在使用的版本是91.0.xxxx.xxx,完美支持:

    當下階段,已完全無需考慮瀏覽器兼容問題,所以JSONP的優(yōu)勢也就不復存在,可以放心的、積極的擁抱CORS。既然要用CORS,作為程序員不能只停留在概念上層面,接下來就來聊點干的,看看從實操層面有哪些具體做法落地CORS呢?

    CORS的核心要義是和服務端和瀏覽器進行溝通,服務端架構一般是分層的,理論上可以在任意層次完成溝通。從負責完成溝通的層次上來講,一般分為這兩大類:

    1. 代理服務器/網(wǎng)關負責
    2. Web應用自行負責

    代理服務器/網(wǎng)關方

    式眾所周知,一般的架構不會是瀏覽器->后端服務點對點, 而是會設計(很多)中間層,比如代理服務器、網(wǎng)關等。就像這樣:

    既然如此,我們就多了一些手段來處理Cors。

    從“距離”上看,我們可以在離瀏覽器最近的地方(流量入口處如Nginx,Gateway等)把Cors跨域問題搞定,這樣后端Web Server就無需再操心了,可謂十分方便。

    下面以Nginx為例,看看如何落地?

     
     
     
     
    1.  
    2. # Wide-open CORS config for nginx ### 沒有保護的(潛臺詞:有安全風險的)NG Cors配置 
    3. location / { 
    4.   
    5.   ### 在Ng層就把Options請求全部攔截掉,不會下層到后面的web應用 
    6.      if ($request_method = 'OPTIONS') { 
    7.       
    8.         ### 使用*通配符表示允許所有的Origin源 
    9.         add_header 'Access-Control-Allow-Origin' '*'; 
    10.         # 
    11.         # Om nom nom cookies 
    12.         # 
    13.         add_header 'Access-Control-Allow-Credentials' 'true'; 
    14.         add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS'; ### 若有需要,可增加PUT、DELETE等請求 
    15.  
    16.         # 
    17.         # Custom headers and headers various browsers *should* be OK with but aren't 
    18.         # 
    19.         ### 允許自定義的請求頭(根據(jù)需要,自行刪減哈) 
    20.         add_header 'Access-Control-Allow-Headers' 'DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type'; 
    21.  
    22.         # 
    23.         # Tell client that this pre-flight info is valid for 20 days 
    24.         # 
    25.         ### 允許預檢請求緩存20天之久(根據(jù)需要自行調整) 
    26.         add_header 'Access-Control-Max-Age' 1728000; 
    27.         add_header 'Content-Type' 'text/plain charset=UTF-8'; 
    28.         add_header 'Content-Length' 0; 
    29.         return 204; 
    30.      } 
    31.   
    32.   ### 因為上面OPTIONS只允許了GET/POST所以這里就只列出兩,根據(jù)需要自行增減哦 ### 
    33.      if ($request_method = 'POST') { 
    34.         add_header 'Access-Control-Allow-Origin' '*'; 
    35.         add_header 'Access-Control-Allow-Credentials' 'true'; 
    36.         add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS'; 
    37.         add_header 'Access-Control-Allow-Headers' 'DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type'; 
    38.      } 
    39.      if ($request_method = 'GET') { 
    40.         add_header 'Access-Control-Allow-Origin' '*'; 
    41.         add_header 'Access-Control-Allow-Credentials' 'true'; 
    42.         add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS'; 
    43.         add_header 'Access-Control-Allow-Headers' 'DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type'; 
    44.      } 

    這是一段比較“著名”的、通用的Nginx解決Cors問題的配置。這段配置基本能夠解決絕大多數(shù)的跨域請求case,但也正是因為它的通用性,帶有如下不足:

    1. Access-Control-Allow-Origin為通配符*,表示所有的Origin都能訪問本站資源,安全性低
    2. Access-Control-Allow-Origin響應頭只允許有一個(有多個就會報錯),而把它寫進了NG,導致后端Web應用無法對它進行精細化控制了
    3. Access-Control-Allow-Credentials的值恒定設置為true。在本系列第二篇文章提到:當需要跨域請求攜帶cookie等驗證信息時,Access-Control-Allow-Origin頭的值是不允許為*的,而NG這一層對此又限制了

    總而言之言而總之,在離瀏覽器最近的地方處理Cors有優(yōu)有劣。優(yōu)點是通用性很好、“體驗”也最好(web server無需感知),但也應當知曉它的劣勢,如安全性低、個性化性差(因為無法感知到業(yè)務需求嘛)。萬物具有兩面性,請勿一刀切,要因地制宜呀。

    一般來講純前端靜態(tài)資源的跨域資源共享可用Ng形式統(tǒng)一處理,但對于服務端(后端)Web應用的API接口資源管理,由于場景較為復雜,對安全性要求頗高,因此還是交給給應用自行管理更為合適

    Gateway網(wǎng)關方式

    網(wǎng)關也可認為是一種代理服務器,屬于中間層中的一層。不過相較于Nginx來講,它的可編程性更強一些,因此很多時候將Cors邏輯放到網(wǎng)關層具有更大的靈活性(特別是內(nèi)網(wǎng)網(wǎng)關),起到一個折中的效果。

    Web應用方式

    Web應用是離瀏覽器“最遠”的地方,在這里解決Cors對應用侵入性最大。但是呢,由于能感知到業(yè)務(如知道有哪些接口、哪些功能)的存在,所以就能做到精細化控制,安全性最高,個性化最強,因此具體落地處理方式也有多種。

    1. 硬編碼方式

    顧名思義,就是在實際處理請求的代碼前/中/后通過硬編碼的方式解決。本系列前面文章給出的代碼示例,為了便于理解均是這種硬編碼方式。

     
     
     
     
    1. /** 
    2.  * 在此處添加備注信息 
    3.  * 
    4.  * @author YourBatman. Send email to me 
    5.  * @site https://yourbatman.cn 
    6.  * @date 2021/6/9 10:36 
    7.  * @since 0.0.1 
    8.  */ 
    9. @Slf4j 
    10. @WebServlet(urlPatterns = "/cors") 
    11. public class CorsServlet extends HttpServlet { 
    12.  
    13.     @Override 
    14.     protected void doOptions(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { 
    15.         super.doOptions(req, resp); 
    16.         setCrosHeader(resp); 
    17.     } 
    18.  
    19.     @Override 
    20.     protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { 
    21.         String requestURI = req.getRequestURI(); 
    22.         String method = req.getMethod(); 
    23.         String originHeader = req.getHeader("Origin"); 
    24.         log.info("收到請求:{},方法:{}, Origin頭:{}", requestURI, method, originHeader); 
    25.  
    26.         resp.getWriter().write("hello cors..."); 
    27.         setCrosHeader(resp); 
    28.     } 
    29.  
    30.     private void setCrosHeader(HttpServletResponse resp) { 
    31.         resp.setHeader("Access-Control-Allow-Origin", "http://localhost:63342"); 
    32.         resp.setHeader("Access-Control-Expose-Headers", "token,secret"); 
    33.         resp.setHeader("Access-Control-Allow-Headers", "token,secret"); // 一般來講,讓此頭的值是上面那個的【子集】(或相同) 
    34.     } 

    優(yōu)點:個性化極強,可以針對接口級別給出不同的CORS邏輯,精細化控制缺點:侵入性從應用級別上升到了業(yè)務代碼級別,顯得十分臃腫,粒度太細后期維護成本高

    2. 自定義Filter/Interceptor

    既然是Filter那便屬于“批處理”方案:對整個應用做Cors的統(tǒng)一邏輯處理

     
     
     
     
    1. /** 
    2.  * 在此處添加備注信息 
    3.  * 
    4.  * @author YourBatman. Send email to me 
    5.  * @site https://yourbatman.cn 
    6.  * @date 2021/6/14 09:50 
    7.  * @since 0.0.1 
    8.  */ 
    9. public class CORSFilter implements Filter { 
    10.  
    11.     @Override 
    12.     public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { 
    13.         HttpServletResponse resp = (HttpServletResponse) response; 
    14.         resp.addHeader("Access-Control-Allow-Credentials", "true"); 
    15.         resp.addHeader("Access-Control-Allow-Origin", "*"); 
    16.         resp.addHeader("Access-Control-Allow-Methods", "GET, POST, DELETE, PUT"); 
    17.         resp.addHeader("Access-Control-Allow-Headers", "Content-Type,X-CAF-Authorization-Token,sessionToken,X-TOKEN"); 
    18.         if (((HttpServletRequest) request).getMethod().equals("OPTIONS")) { 
    19.             resp.getWriter().println("ok"); 
    20.             return; 
    21.         } 
    22.         chain.doFilter(request, resp); 
    23.     } 
    24.      

    優(yōu)點:應用級別的統(tǒng)一處理,對業(yè)務代碼無侵入性。應用內(nèi)集中化處理Cors邏輯,維護方便缺點:無法做到接口級別的粒度,對于某些特殊要求的細粒度控制自然就無能為力

    說到底,上例中的自定義Filter的方式仍屬于硬編碼方式(將影響Cors的相關頭信息寫死的),不夠靈活。其實可以再優(yōu)化一下,讓其更富彈性。為此,早在N年之前就有eBay開源的過濾器方案:cors-filter.java 供以參考。

     
     
     
     
    1.  
    2.  org.ebaysf.web 
    3.  cors-filter 
    4.  1.0.1 
    5.  

    它可以讓允許的origins、methods、headers等都支持可配置化,更富彈性。

    3. Spring Framework方式

    調研一下:現(xiàn)在做Java(Web)開發(fā),應該沒有不使用Spring Framework的吧?

    Spring自4.2版本(2015-06)開始,就提供了對Cors的全面支持,大大簡化應用級Cors問題的處理。其中面向開發(fā)者提供了兩個用于優(yōu)雅處理Cors問題的組件:

    • @CrossOrigin:借助此注解可以通過聲明式方式,對類級別、甚至接口級別進行跨域的資源控制
    • CorsFilter:Spring也提供了用于“全局處理”的過濾器,兼具了普適性和靈活性

    WebMvcConfigurer:這是一種配置方式,嚴格來講不算一種解決方案而是一種落地方式而已

    由于Java開發(fā)者一直和Spring打交道,因此深入理解此場景下的解決方案,打通其執(zhí)行原理方可使用起來得心應手,所以這也是本系列關心的重中之重。關于此part本系列下文會單獨成篇解讀,包括使用姿勢到設計思想、源碼分析.....

    4. Spring Boot方式

    如你所知,Spring Boot是構建在Spring Framework之上的。在Cors這塊Spring Boot并未對其做增強or擴展,因此使用姿勢上同Spring Framework。

    這是不是再一次驗證了那句話:在Spring Boot上能走多遠由你對Spring Framework的了解深度而決定

    Cors安全漏洞

    瀏覽器的同源策略(SOP)是一個安全基石。SOP是一個很好的策略,但是隨著Web應用的發(fā)展,網(wǎng)站由于自身業(yè)務的需求,需要實現(xiàn)一些跨域的功能,能夠讓不同域的頁面之間能夠相互訪問各自頁面的內(nèi)容,這就導致SOP策略不是那么的湊效了。

    Cors作為當下解決瀏覽器跨域問題的標準方案,如若使用不當是會帶來安全漏洞,造成隱患的。其中最常見的便是:Access-Control-Allow-Origin: *到底。殊不知,*用于表示允許任意域訪問,這種配置一般只用于共享公開資源。如果用于非公共資源的話,那就相當于擊穿了瀏覽器的同源策略,給所有Origin授權。

    其實這和授權授信有點像,當授權范圍越大,方便的是操作/管理上,但這就容易被利用而被攻擊。因此在允許的情況下,能粒度小點就盡量精細化控制(特別是敏感資源、接口),畢竟安全無小事。

    Access-Control-Allow-Origin既然不建議配置為*,那么如何允許多域名呢?本系列上篇文章有詳細分析,請參考:Access-Control-Allow-Origin

    安全性這個東西是相對的,沒有絕對的安全,也做不到絕對的安全。我們能做的,就是盡量去解決已知的安全性問題,不要讓“入侵”來得很容易即可。

    JSONP與CORS對比

    JSONP與CORS的使用目的相同,并且都需要服務端和客戶端同時支持,雖然功能上講CORS更為強大,但......下面進行對比下

    1.JSONP的最主要優(yōu)勢是對(老)瀏覽器的支持很好,而CORS由于出現(xiàn)較晚(2014年確定)這方面稍差~

    • 不過,還是那句話:現(xiàn)在都2021年了,在瀏覽器支持方面可以幾乎不用再作考慮

    2.JSONP 只能 用于Get請求,而CORS能用于所有的Http Method。這一點上JSONP被完虐

    3.JSONP的錯誤處理機制不完善(其實是沒有),當發(fā)生錯誤時開發(fā)者無法進行處理。而CORS可以通過onerror監(jiān)聽到錯誤事件,從而就可以看到錯誤詳情方便排查問題

    4.JSONP只會發(fā)送一次請求,而CORS的非簡單請求會發(fā)送兩次(大部分情況下的請求都會屬于非簡單請求)

    • 還不懂什么是簡單請求和非簡單請求,看本系列第一篇:Cors跨域(一):深入理解跨域請求概念及其根因

    5.安全問題上,二者也有較大差異:

    • JSONP不是跨域的規(guī)范,它存在明顯的安全漏洞。表現(xiàn)在:callback參數(shù)注入(這是由于這些元素都是裸露的),以及資源授權方面無法限制(也就說他能接受所有Origin的請求從而也不太安全)
    • CORS是跨域的規(guī)范,并且能夠對資源授權方面做控制。Access-Control-Allow-Origin響應頭就是最重要的一個響應頭,當然嘍若你把它恒定設為*,那它的安全性就大大退化

    總的來講,CORS相較于JSONP 優(yōu)勢明顯 ,在實際生產(chǎn)使用上,忘了JSONP吧

    總結

    JSONP作為解決跨域問題的曾經(jīng)的唯一方案,立下汗馬功勞,現(xiàn)在是該退役了。但我們有理由記得它,畢竟英雄遲暮也希望不被遺忘(扯淡了,主要是這個名詞在很多新/老文章中還經(jīng)常被提起,注意分辨不要被弄迷糊啦)。

    總而言之,作為新時代的開發(fā)人員,心里可認為跨域問題的解決方案只有一種:那便是Cors。下一篇將是“激動人心”的內(nèi)容:講述Cors在Spring環(huán)境中的實施,見識下那有多優(yōu)雅吧

    本文轉載自微信公眾號「BAT的烏托邦」,可以通過以下二維碼關注。轉載本文請聯(lián)系BAT的烏托邦公眾號。


    新聞標題:Cors跨域(四):解決方案對決JSONP vs CORS
    網(wǎng)頁鏈接:http://m.5511xx.com/article/cdpghgp.html