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

跨域問題是前后端分離項(xiàng)目中非常常見的一個問題檀训,舉例來說柑潦,編程貓(codingmore)學(xué)習(xí)網(wǎng)站的前端服務(wù)跑在 8080 端口下,后端服務(wù)跑在 9002 端口下峻凫,那么前端在請求后端接口的時候就會出現(xiàn)跨域問題渗鬼。

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

那怎么解決這個問題呢?通常有兩個思路:

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

一堰乔、關(guān)于跨域

跨域?qū)τ谇昂蠖碎_發(fā)者來說,就像一塊狗皮膏藥脐恩,無論是面試還是開發(fā)中镐侯,都會經(jīng)常遇到。

之所以出現(xiàn)跨域問題驶冒,是因?yàn)?a target="_blank">瀏覽器的同源策略苟翻,為了隔離潛在的惡意文件韵卤,為了防御來自歪門邪道的攻擊,瀏覽器限制了從同一個源加載的文檔或腳本與來自另一個源的資源進(jìn)行交互崇猫。

前面我們提到了沈条,前端跑在 8080 端口下,后端跑在 9002 端口下诅炉,這種情況就屬于不同的源(域名不同蜡歹,協(xié)議不同,端口不同)涕烧,所以 8080 端口下的前端請求直接訪問 9002 端口下的后端接口時就訪問失敗了月而。

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

二痹扇、Nodejs 代理

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

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

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

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

第二步劣像,配置前端訪問請求路徑

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

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

再次點(diǎn)擊「登錄」按鈕摧玫,可以看到請求的 URL 發(fā)生了改變耳奕,原來是 http://localhost:9002/users/login,現(xiàn)在是 http://localhost:8080/api/users/login。與此同時吮铭,可以看到多了一個 Remote Address时迫,端口也是 8080,也就是說經(jīng)過 Nodejs 的代理谓晌,前后端的交互在同一個源下面了,這樣就不會發(fā)生跨域問題了癞揉。

同時纸肉,可以看得到,服務(wù)器端返回的狀態(tài)碼變成了 200喊熟,表示請求成功柏肪。

三、開啟跨域資源共享

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

第一步弃理,開啟 CORS 支持

在 Spring Boot 應(yīng)用中溃论,加入 CORS 的支持簡單到不忍直視,添加一個配置類就可以了痘昌。

@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("*");
        //允許所有請求方法跨域調(diào)用
        config.addAllowedMethod("*");
        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        source.registerCorsConfiguration("/**", config);
        return new CorsFilter(source);
    }
}

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

本例中,后端返回 Access-Control-Allow-Origin: http://localhost:8080 就表示驻啤,跑在 9002 端口下的后端接口可以被 8080 端口的前端請求訪問菲驴。

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

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

對于 login 這種簡單的請求來說谢翎,它們是不會觸發(fā) CORS 預(yù)檢的,因此不需要在服務(wù)器端增加其他配置就可以了沐旨。那什么是簡單請求呢森逮?

1)請求方法是以下三種方法之一:

  • HEAD
  • GET
  • POST

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

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

那對于會觸發(fā) CORS 預(yù)檢的非簡單請求(比如說請求方法是 PUT 或 DELETE褒侧,或者 Content-Type 字段的類型是 application/json,或者請求消息頭包含了一些自定義的字段),該怎么辦呢闷供?

非簡單請求在正式通信之前烟央,會增加一次 HTTP 查詢請求,稱為“預(yù)檢”請求歪脏。預(yù)檢請求通過后疑俭,才會返回正常的響應(yīng)內(nèi)容。

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

第三步砚著,通過以下代碼給 OPTIONS 請求放行次伶。

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

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

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

我們來通過兩張圖片簡單總結(jié)一下預(yù)檢請求的整個過程乎折,第一張绒疗,發(fā)起 OPTIONS 預(yù)檢請求:

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

四骂澄、源碼路徑

編程貓后端源碼:

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

編程貓后臺管理的前端源碼:

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
簡單請求+預(yù)檢請求:https://github.com/amandakelake/blog/issues/62


本篇已收錄至 GitHub 上星標(biāo) 1.6k+ star 的開源專欄《Java 程序員進(jìn)階之路》吓蘑,據(jù)說每一個優(yōu)秀的 Java 程序員都喜歡她,風(fēng)趣幽默坟冲、通俗易懂磨镶。內(nèi)容包括 Java 基礎(chǔ)、Java 并發(fā)編程健提、Java 虛擬機(jī)琳猫、Java 企業(yè)級開發(fā)、Java 面試等核心知識點(diǎn)私痹。學(xué) Java脐嫂,就認(rèn)準(zhǔn) Java 程序員進(jìn)階之路??统刮。

https://github.com/itwanger/toBeBetterJavaer

star 了這個倉庫就等于你擁有了成為了一名優(yōu)秀 Java 工程師的潛力。也可以戳下面的鏈接跳轉(zhuǎn)到《Java 程序員進(jìn)階之路》的官網(wǎng)網(wǎng)址账千,開始愉快的學(xué)習(xí)之旅吧侥蒙。

https://tobebetterjavaer.com/

image

沒有什么使我停留——除了目的,縱然岸旁有玫瑰匀奏、有綠蔭鞭衩、有寧靜的港灣,我是不系之舟攒射。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末醋旦,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子会放,更是在濱河造成了極大的恐慌,老刑警劉巖钉凌,帶你破解...
    沈念sama閱讀 217,734評論 6 505
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件咧最,死亡現(xiàn)場離奇詭異,居然都是意外死亡御雕,警方通過查閱死者的電腦和手機(jī)矢沿,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,931評論 3 394
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來酸纲,“玉大人捣鲸,你說我怎么就攤上這事∶銎拢” “怎么了栽惶?”我有些...
    開封第一講書人閱讀 164,133評論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長疾嗅。 經(jīng)常有香客問我外厂,道長,這世上最難降的妖魔是什么代承? 我笑而不...
    開封第一講書人閱讀 58,532評論 1 293
  • 正文 為了忘掉前任汁蝶,我火速辦了婚禮,結(jié)果婚禮上论悴,老公的妹妹穿的比我還像新娘掖棉。我一直安慰自己,他們只是感情好膀估,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,585評論 6 392
  • 文/花漫 我一把揭開白布幔亥。 她就那樣靜靜地躺著,像睡著了一般玖像。 火紅的嫁衣襯著肌膚如雪紫谷。 梳的紋絲不亂的頭發(fā)上齐饮,一...
    開封第一講書人閱讀 51,462評論 1 302
  • 那天,我揣著相機(jī)與錄音笤昨,去河邊找鬼祖驱。 笑死,一個胖子當(dāng)著我的面吹牛瞒窒,可吹牛的內(nèi)容都是我干的捺僻。 我是一名探鬼主播,決...
    沈念sama閱讀 40,262評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼崇裁,長吁一口氣:“原來是場噩夢啊……” “哼匕坯!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起拔稳,我...
    開封第一講書人閱讀 39,153評論 0 276
  • 序言:老撾萬榮一對情侶失蹤葛峻,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后巴比,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體术奖,經(jīng)...
    沈念sama閱讀 45,587評論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,792評論 3 336
  • 正文 我和宋清朗相戀三年轻绞,在試婚紗的時候發(fā)現(xiàn)自己被綠了采记。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,919評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡政勃,死狀恐怖唧龄,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情奸远,我是刑警寧澤既棺,帶...
    沈念sama閱讀 35,635評論 5 345
  • 正文 年R本政府宣布,位于F島的核電站然走,受9級特大地震影響援制,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜芍瑞,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,237評論 3 329
  • 文/蒙蒙 一晨仑、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧拆檬,春花似錦洪己、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,855評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至屑那,卻和暖如春拱镐,著一層夾襖步出監(jiān)牢的瞬間艘款,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,983評論 1 269
  • 我被黑心中介騙來泰國打工沃琅, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留哗咆,地道東北人。 一個月前我還...
    沈念sama閱讀 48,048評論 3 370
  • 正文 我出身青樓益眉,卻偏偏與公主長得像晌柬,于是被迫代替她去往敵國和親辙谜。 傳聞我的和親對象是個殘疾皇子浸剩,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,864評論 2 354

推薦閱讀更多精彩內(nèi)容