<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          前后端分離項(xiàng)目,如何解決跨域問(wèn)題?

          共 4490字,需瀏覽 9分鐘

           ·

          2022-02-26 17:18

          跨域問(wèn)題是前后端分離項(xiàng)目中非常常見(jiàn)的一個(gè)問(wèn)題,舉例來(lái)說(shuō),編程貓(codingmore)學(xué)習(xí)網(wǎng)站的前端服務(wù)跑在 8080 端口下,后端服務(wù)跑在 9002 端口下,那么前端在請(qǐng)求后端接口的時(shí)候就會(huì)出現(xiàn)跨域問(wèn)題。

          403 Forbidden 是HTTP協(xié)議中的一個(gè)狀態(tài)碼(Status Code),意味著后端服務(wù)雖然成功解析了請(qǐng)求,但前端卻沒(méi)有訪問(wèn)該資源的權(quán)限。

          那怎么解決這個(gè)問(wèn)題呢?通常有兩個(gè)思路:

          • 前端使用 Nodejs 代理(開(kāi)發(fā)環(huán)境下,生產(chǎn)環(huán)境下可以用 Nginx 替代)
          • 或者后端開(kāi)啟跨域資源共享

          一、關(guān)于跨域

          跨域?qū)τ谇昂蠖碎_(kāi)發(fā)者來(lái)說(shuō),就像一塊狗皮膏藥,無(wú)論是面試還是開(kāi)發(fā)中,都會(huì)經(jīng)常遇到。

          之所以出現(xiàn)跨域問(wèn)題,是因?yàn)闉g覽器的同源策略,為了隔離潛在的惡意文件,為了防御來(lái)自歪門(mén)邪道的攻擊,瀏覽器限制了從同一個(gè)源加載的文檔或腳本與來(lái)自另一個(gè)源的資源進(jìn)行交互。

          前面我們提到了,前端跑在 8080 端口下,后端跑在 9002 端口下,這種情況就屬于不同的源(域名不同,協(xié)議不同,端口不同),所以 8080 端口下的前端請(qǐng)求直接訪問(wèn) 9002 端口下的后端接口時(shí)就訪問(wèn)失敗了。

          那正確的打開(kāi)方式是什么呢?我們前面也提到了,前端使用 Nodejs 代理或者后端開(kāi)啟跨域資源共享,我們一一來(lái)實(shí)踐下。

          二、Nodejs 代理

          在 Nodejs 出現(xiàn)之前,JavaScript 編寫(xiě)的程序通常需要在用戶(hù)的瀏覽器上執(zhí)行,Node.js 出現(xiàn)后,JavaScript 也能用于服務(wù)端編程了。Nodejs 一系列的內(nèi)置模塊使得程序可以脫離 IIS、Apache 這種 Web 服務(wù)作為獨(dú)立的服務(wù)器執(zhí)行。

          我們使用 Nodejs 來(lái)解決跨域問(wèn)題的思路就是,在本地創(chuàng)建一個(gè)虛擬服務(wù)器,對(duì) 8080 端口下的前端請(qǐng)求進(jìn)行代理,同時(shí)接收 9002 端口下的服務(wù)器端響應(yīng),這樣服務(wù)端和服務(wù)端進(jìn)行數(shù)據(jù)的交互就不會(huì)出現(xiàn)跨域問(wèn)題了。

          第一步,配置 Nodejs 代理服務(wù)

          module.exports?=?{
          ??dev:?{
          ????//?Paths
          ????assetsSubDirectory:?'static',
          ????assetsPublicPath:?'/',
          ????proxyTable:?{
          ??????'/api':?{
          ????????target:?'http://localhost:9002',?//?你請(qǐng)求的第三方接口
          ????????changeOrigin:?false,?//?在本地會(huì)創(chuàng)建一個(gè)虛擬服務(wù)端,然后發(fā)送請(qǐng)求的數(shù)據(jù),并同時(shí)接收請(qǐng)求的數(shù)據(jù),這樣服務(wù)端和服務(wù)端進(jìn)行數(shù)據(jù)的交互就不會(huì)有跨域問(wèn)題
          ????????pathRewrite:?{?//?路徑重寫(xiě),
          ??????????'^/api':?''?//?替換target中的請(qǐng)求地址,也就是說(shuō)以后你在請(qǐng)求http://api.codingmore.top/v2/XXXXX這個(gè)地址的時(shí)候直接寫(xiě)成/api即可。
          ????????}
          ??????},
          ????},
          }

          第二步,配置前端訪問(wèn)請(qǐng)求路徑

          module.exports?=?merge(prodEnv,?{
          ??NODE_ENV:?'"development"',
          ??VUE_APP_BASE_API:?'"/api"'
          ??//?VUE_APP_BASE_API:?'"http://localhost:9002"'
          })

          第三步,重啟前端服務(wù)

          再次點(diǎn)擊「登錄」按鈕,可以看到請(qǐng)求的 URL 發(fā)生了改變,原來(lái)是 http://localhost:9002/users/login,現(xiàn)在是 http://localhost:8080/api/users/login。與此同時(shí),可以看到多了一個(gè) Remote Address,端口也是 8080,也就是說(shuō)經(jīng)過(guò) Nodejs 的代理,前后端的交互在同一個(gè)源下面了,這樣就不會(huì)發(fā)生跨域問(wèn)題了。

          同時(shí),可以看得到,服務(wù)器端返回的狀態(tài)碼變成了 200,表示請(qǐng)求成功。

          三、開(kāi)啟跨域資源共享

          跨域資源共享,也就是 Cross-Origin Resource Sharing,簡(jiǎn)拼為 CORS,是一種基于 HTTP 頭信息的機(jī)制,通過(guò)允許服務(wù)器標(biāo)識(shí)除了它自己以外的資源,從而實(shí)現(xiàn)跨域訪問(wèn)。

          第一步,開(kāi)啟 CORS 支持

          在 Spring Boot 應(yīng)用中,加入 CORS 的支持簡(jiǎn)單到不忍直視,添加一個(gè)配置類(lèi)就可以了。

          @Configuration
          public?class?GlobalCorsConfig?{
          ????@Bean
          ????public?CorsFilter?corsFilter()?{
          ????????CorsConfiguration?config?=?new?CorsConfiguration();
          ????????//?設(shè)置你要允許的網(wǎng)站域名
          ????????config.addAllowedOrigin("http://localhost:8080");
          ????????//允許跨域發(fā)送cookie
          ????????config.setAllowCredentials(true);
          ????????//放行全部原始頭信息
          ????????config.addAllowedHeader("*");
          ????????//允許所有請(qǐng)求方法跨域調(diào)用
          ????????config.addAllowedMethod("*");
          ????????UrlBasedCorsConfigurationSource?source?=?new?UrlBasedCorsConfigurationSource();
          ????????source.registerCorsConfiguration("/**",?config);
          ????????return?new?CorsFilter(source);
          ????}
          }

          第二步,重啟后端服務(wù),再次點(diǎn)擊登錄按鈕,發(fā)現(xiàn)請(qǐng)求已經(jīng)可以正常訪問(wèn)了。

          本例中,后端返回 Access-Control-Allow-Origin: http://localhost:8080 就表示,跑在 9002 端口下的后端接口可以被 8080 端口的前端請(qǐng)求訪問(wèn)。

          如果允許所有域名進(jìn)行跨域調(diào)用的話,只需改變一行代碼即可。

          //允許所有域名進(jìn)行跨域調(diào)用
          config.addAllowedOriginPattern("*");
          //?設(shè)置你要允許的網(wǎng)站域名
          //????????config.addAllowedOrigin("http://localhost:8080");

          對(duì)于 login 這種簡(jiǎn)單的請(qǐng)求來(lái)說(shuō),它們是不會(huì)觸發(fā) CORS 預(yù)檢的,因此不需要在服務(wù)器端增加其他配置就可以了。那什么是簡(jiǎn)單請(qǐng)求呢?

          1)請(qǐng)求方法是以下三種方法之一:

          • HEAD
          • GET
          • POST

          2)HTTP 的頭信息不超出以下幾種字段:

          • Accept
          • Accept-Language
          • Content-Language
          • Last-Event-ID
          • Content-Type:只限于三個(gè)值 application/x-www-form-urlencoded、multipart/form-data、text/plain

          那對(duì)于會(huì)觸發(fā) CORS 預(yù)檢的非簡(jiǎn)單請(qǐng)求(比如說(shuō)請(qǐng)求方法是 PUT 或 DELETE,或者 Content-Type 字段的類(lèi)型是 application/json,或者請(qǐng)求消息頭包含了一些自定義的字段),該怎么辦呢?

          非簡(jiǎn)單請(qǐng)求在正式通信之前,會(huì)增加一次 HTTP 查詢(xún)請(qǐng)求,稱(chēng)為“預(yù)檢”請(qǐng)求。預(yù)檢請(qǐng)求通過(guò)后,才會(huì)返回正常的響應(yīng)內(nèi)容。

          拿編程貓的文章管理頁(yè)來(lái)舉例,該頁(yè)面會(huì)向后端發(fā)起一個(gè) posts/queryPageable 的分頁(yè)查詢(xún),該請(qǐng)求包含了一個(gè)自定義的消息頭 Authorization,于是瀏覽器認(rèn)為該請(qǐng)求是一個(gè)非簡(jiǎn)單請(qǐng)求,然后就會(huì)自動(dòng)發(fā)起一次 OPTIONS 請(qǐng)求,但由于我們的 Spring Boot 項(xiàng)目整合了 SpringsScurity 安全管理框架,沒(méi)有對(duì)OPTIONS請(qǐng)求放開(kāi)登錄認(rèn)證,導(dǎo)致驗(yàn)證失敗,文章分頁(yè)請(qǐng)求的響應(yīng)數(shù)據(jù)就沒(méi)有返回回來(lái)。

          第三步,通過(guò)以下代碼給 OPTIONS 請(qǐng)求放行。

          public?class?SecurityConfig?extends?WebSecurityConfigurerAdapter?{
          ????@Override
          ????protected?void?configure(HttpSecurity?httpSecurity)?throws?Exception?{
          ????????ExpressionUrlAuthorizationConfigurer.ExpressionInterceptUrlRegistry?registry?=?httpSecurity
          ????????????????.authorizeRequests();
          ????????//允許跨域請(qǐng)求的OPTIONS請(qǐng)求
          ????????registry.antMatchers(HttpMethod.OPTIONS)
          ????????????????.permitAll();
          ????}
          }

          再次重啟后端服務(wù),重新訪問(wèn)文章列表接口,發(fā)現(xiàn)有響應(yīng)數(shù)據(jù)了。

          非簡(jiǎn)單請(qǐng)求必須首先使用 OPTIONS 請(qǐng)求方法發(fā)起一個(gè)預(yù)檢請(qǐng)求到服務(wù)器端,以獲知服務(wù)器是否允許該實(shí)際請(qǐng)求。"預(yù)檢請(qǐng)求“的使用,避免了跨域請(qǐng)求對(duì)服務(wù)器的用戶(hù)數(shù)據(jù)造成未預(yù)期的影響。

          我們來(lái)通過(guò)兩張圖片簡(jiǎn)單總結(jié)一下預(yù)檢請(qǐng)求的整個(gè)過(guò)程,第一張,發(fā)起 OPTIONS 預(yù)檢請(qǐng)求:

          第二章,發(fā)起正式請(qǐng)求:

          四、源碼路徑

          編程貓后端源碼:

          https://github.com/itwanger/coding-more

          編程貓后臺(tái)管理的前端源碼:

          https://github.com/itwanger/codingmore-admin-web

          參考鏈接:

          • 跨域:https://segmentfault.com/a/1190000015597029
          • CORS:https://developer.mozilla.org/zh-CN/docs/Web/HTTP/CORS
          • 阮一峰:https://www.ruanyifeng.com/blog/2016/04/cors.html
          • 簡(jiǎn)單請(qǐng)求+預(yù)檢請(qǐng)求:https://github.com/amandakelake/blog/issues/62

          沒(méi)有什么使我停留——除了目的,縱然岸旁有玫瑰、有綠蔭、有寧?kù)o的港灣,我是不系之舟

          推薦閱讀

          瀏覽 25
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  成人欧美一区二区三区男男 | A片操逼| 蜜乳AV一区二区三区 | 国产1区2区3区 | 亚洲一区欧美国产日韩 云播 |