<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>

          Spring Boot 解決跨域問(wèn)題的 3 種方案!

          共 6991字,需瀏覽 14分鐘

           ·

          2021-09-03 12:28

          作者 | telami

          來(lái)源 | telami.cn/2019/springboot-resolve-cors


          前后端分離大勢(shì)所趨,跨域問(wèn)題更是老生常談,隨便用標(biāo)題去google或百度一下,能搜出一大片解決方案,那么為啥又要寫一遍呢,不急往下看。

          問(wèn)題背景:

          Same Origin Policy,譯為“同源策略”。它是對(duì)于客戶端腳本(尤其是JavaScript)的重要安全度量標(biāo)準(zhǔn),其目的在于防止某個(gè)文檔或者腳本從多個(gè)不同“origin”(源)裝載。它認(rèn)為自任何站點(diǎn)裝載的信賴內(nèi)容是不安全的。
          當(dāng)被瀏覽器半信半疑的腳本運(yùn)行在沙箱時(shí),它們應(yīng)該只被允許訪問(wèn)來(lái)自同一站點(diǎn)的資源,而不是那些來(lái)自其它站點(diǎn)可能懷有惡意的資源。
          注:具有相同的Origin,也即是擁有相同的協(xié)議、主機(jī)地址以及端口。一旦這三項(xiàng)數(shù)據(jù)中有一項(xiàng)不同,那么該資源就將被認(rèn)為是從不同的Origin得來(lái)的,進(jìn)而不被允許訪問(wèn)。
          CORS就是為了解決SOP問(wèn)題而生的,當(dāng)然CORS不是唯一的解決方案,不過(guò)這里不贅述其他解決辦法了。

          CORS簡(jiǎn)介:

          CORS是一個(gè)W3C標(biāo)準(zhǔn),全稱是"跨域資源共享”(Cross-origin resource sharing)。它允許瀏覽器向跨源(協(xié)議 + 域名 + 端口)服務(wù)器,發(fā)出XMLHttpRequest請(qǐng)求,從而克服了AJAX只能同源使用的限制。CORS需要瀏覽器和服務(wù)器同時(shí)支持。它的通信過(guò)程,都是瀏覽器自動(dòng)完成,不需要用戶參與。
          對(duì)于開發(fā)者來(lái)說(shuō),CORS通信與同源的AJAX/Fetch通信沒有差別,代碼完全一樣。瀏覽器一旦發(fā)現(xiàn)請(qǐng)求跨源,就會(huì)自動(dòng)添加一些附加的頭信息,有時(shí)還會(huì)多出一次附加的請(qǐng)求,但用戶不會(huì)有感覺。因此,實(shí)現(xiàn)CORS通信的關(guān)鍵是服務(wù)器。只要服務(wù)器實(shí)現(xiàn)了CORS接口,就可以跨源通信。
          瀏覽器將CORS請(qǐng)求分成兩類:簡(jiǎn)單請(qǐng)求(simple request)和非簡(jiǎn)單請(qǐng)求(not-so-simple request)。
          瀏覽器發(fā)出CORS簡(jiǎn)單請(qǐng)求,只需要在頭信息之中增加一個(gè)Origin字段。
          瀏覽器發(fā)出CORS非簡(jiǎn)單請(qǐng)求,會(huì)在正式通信之前,增加一次OPTIONS查詢請(qǐng)求,稱為"預(yù)檢"請(qǐng)求(preflight)。瀏覽器先詢問(wèn)服務(wù)器,當(dāng)前網(wǎng)頁(yè)所在的域名是否在服務(wù)器的許可名單之中,以及可以使用哪些HTTP動(dòng)詞和頭信息字段。只有得到肯定答復(fù),瀏覽器才會(huì)發(fā)出正式的XMLHttpRequest請(qǐng)求,否則就報(bào)錯(cuò)。
          簡(jiǎn)單請(qǐng)求就是HEAD、GET、POST請(qǐng)求,并且HTTP的頭信息不超出以下幾種字段 Accept、Accept-Language、Content-Language、Last-Event-ID、Content-Type 注:Content-Type:只限于三個(gè)值application/x-www-form-urlencoded、multipart/form-data、text/plain
          反之,就是非簡(jiǎn)單請(qǐng)求。
          其實(shí)實(shí)現(xiàn)CORS很簡(jiǎn)單,就是在服務(wù)端加一些響應(yīng)頭,并且這樣做對(duì)前端來(lái)說(shuō)是無(wú)感知的,很方便。

          詳解響應(yīng)頭:

          • Access-Control-Allow-Origin 該字段必填。它的值要么是請(qǐng)求時(shí)Origin字段的具體值,要么是一個(gè)*,表示接受任意域名的請(qǐng)求。
          • Access-Control-Allow-Methods 該字段必填。它的值是逗號(hào)分隔的一個(gè)具體的字符串或者*,表明服務(wù)器支持的所有跨域請(qǐng)求的方法。注意,返回的是所有支持的方法,而不單是瀏覽器請(qǐng)求的那個(gè)方法。這是為了避免多次"預(yù)檢"請(qǐng)求。
          • Access-Control-Expose-Headers 該字段可選。CORS請(qǐng)求時(shí),XMLHttpRequest對(duì)象的getResponseHeader()方法只能拿到6個(gè)基本字段:Cache-Control、Content-Language、Content-Type、Expires、Last-Modified、Pragma。如果想拿到其他字段,就必須在Access-Control-Expose-Headers里面指定。
          • Access-Control-Allow-Credentials 該字段可選。它的值是一個(gè)布爾值,表示是否允許發(fā)送Cookie.默認(rèn)情況下,不發(fā)生Cookie,即:false。對(duì)服務(wù)器有特殊要求的請(qǐng)求,比如請(qǐng)求方法是PUT或DELETE,或者Content-Type字段的類型是application/json,這個(gè)值只能設(shè)為true。如果服務(wù)器不要瀏覽器發(fā)送Cookie,刪除該字段即可。
          • Access-Control-Max-Age 該字段可選,用來(lái)指定本次預(yù)檢請(qǐng)求的有效期,單位為秒。在有效期間,不用發(fā)出另一條預(yù)檢請(qǐng)求。
          順便提一下,如果在開發(fā)中,發(fā)現(xiàn)每次發(fā)起請(qǐng)求都是兩條,一次OPTIONS,一次正常請(qǐng)求,注意是每次,那么就需要配置Access-Control-Max-Age,避免每次都發(fā)出預(yù)檢請(qǐng)求。

          解決辦法:

          第一種辦法:
          import org.springframework.context.annotation.Configuration;
          import org.springframework.web.servlet.config.annotation.CorsRegistry;
          import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
          @Configuration
          public class CorsConfig implements WebMvcConfigurer {
              @Override
              public void addCorsMappings(CorsRegistry registry) {
                  registry.addMapping("/**")
                          .allowedOrigins("*")
                          .allowedMethods("GET""HEAD""POST""PUT""DELETE""OPTIONS")
                          .allowCredentials(true)
                          .maxAge(3600)
                          .allowedHeaders("*");
              }
          }
          這種方式是全局配置的,網(wǎng)上也大都是這種解決辦法,但是很多都是基于舊的spring版本,比如 WebMvcConfigurerAdapter 在spring5.0已經(jīng)被標(biāo)記為Deprecated,點(diǎn)開源碼可以看到:
          /**
           * An implementation of {@link WebMvcConfigurer} with empty methods allowing
           * subclasses to override only the methods they're interested in.
           *
           * @author Rossen Stoyanchev
           * @since 3.1
           * @deprecated as of 5.0 {@link WebMvcConfigurer} has default methods (made
           * possible by a Java 8 baseline) and can be implemented directly without the
           * need for this adapter
           */
          @Deprecated
          public abstract class WebMvcConfigurerAdapter implements WebMvcConfigurer {}
          像這種過(guò)時(shí)的類或者方法,spring的作者們一定會(huì)在注解上面說(shuō)明原因,并告訴你新的該用哪個(gè),這是非常優(yōu)秀的編碼習(xí)慣,點(diǎn)贊!
          spring5最低支持到j(luò)dk1.8,所以注釋中明確表明,你可以直接實(shí)現(xiàn)WebMvcConfigurer接口,無(wú)需再用這個(gè)適配器,因?yàn)閖dk1.8支持接口中存在default-method。
          Spring Boot 基礎(chǔ)就不介紹了,看下這個(gè)教程太全了:
          https://github.com/javastacks/spring-boot-best-practice

          第二種辦法:
          import org.springframework.context.annotation.Configuration;
          import javax.servlet.*;
          import javax.servlet.annotation.WebFilter;
          import javax.servlet.http.HttpServletResponse;
          import java.io.IOException;
          @WebFilter(filterName = "CorsFilter ")
          @Configuration
          public class CorsFilter implements Filter {
              @Override
              public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
                  HttpServletResponse response = (HttpServletResponse) res;
                  response.setHeader("Access-Control-Allow-Origin","*");
                  response.setHeader("Access-Control-Allow-Credentials""true");
                  response.setHeader("Access-Control-Allow-Methods""POST, GET, PATCH, DELETE, PUT");
                  response.setHeader("Access-Control-Max-Age""3600");
                  response.setHeader("Access-Control-Allow-Headers""Origin, X-Requested-With, Content-Type, Accept");
                  chain.doFilter(req, res);
              }
          }
          這種辦法,是基于過(guò)濾器的方式,方式簡(jiǎn)單明了,就是在response中寫入這些響應(yīng)頭,好多文章都是第一種和第二種方式都叫你配置,其實(shí)這是沒有必要的,只需要一種即可。
          這里也吐槽一下,大家不求甚解的精神。另外,關(guān)注公眾號(hào)Java技術(shù)棧,在后臺(tái)回復(fù):面試,可以獲取我整理的 Spring Boot 面試題和答案。

          第三種辦法:
          public class GoodsController {
          @CrossOrigin(origins = "http://localhost:4000")
          @GetMapping("goods-url")
          public Response queryGoodsWithGoodsUrl(@RequestParam String goodsUrl) throws Exception {}
          }  
          沒錯(cuò)就是**@CrossOrigin**注解,點(diǎn)開注解
          @Target({ ElementType.METHOD, ElementType.TYPE })
          @Retention(RetentionPolicy.RUNTIME)
          @Documented
          public @interface CrossOrigin {

          }
          從元注解@Target可以看出,注解可以放在method、class等上面,類似RequestMapping,也就是說(shuō),整個(gè)controller下面的方法可以都受控制,也可以單個(gè)方法受控制。
          也可以得知,這個(gè)是最小粒度的cors控制辦法了,精確到單個(gè)請(qǐng)求級(jí)別。

          以上三種方法都可以解決問(wèn)題,最常用的應(yīng)該是第一種、第二種,控制在自家?guī)讉€(gè)域名范圍下足以,一般沒必要搞得太細(xì)。
          這三種配置方式都用了的話,誰(shuí)生效呢,類似css中樣式,就近原則,懂了吧。
          所以在開發(fā)新項(xiàng)目時(shí),不需要等聯(lián)調(diào)時(shí)候,讓前端來(lái)找你了,我早就解決了跨域問(wèn)題。

          逆鋒起筆是一個(gè)專注于程序員圈子的技術(shù)平臺(tái),你可以收獲最新技術(shù)動(dòng)態(tài)最新內(nèi)測(cè)資格BAT等大廠大佬的經(jīng)驗(yàn)增長(zhǎng)自身學(xué)習(xí)資料職業(yè)路線賺錢思維,微信搜索逆鋒起筆關(guān)注!

          40 個(gè) SpringBoot 常用注解

          SpringBoot 是如何實(shí)現(xiàn)自動(dòng)配置的?

          Spring Boot 核心知識(shí),深入剖析!

          SpringBoot 項(xiàng)目啟動(dòng)卡住問(wèn)題排查記錄

          SpringBoot 四大核心組件,你知道幾個(gè)?

          瀏覽 40
          點(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>
                  九色自拍偷拍 | 黄色无码高清 | 天天综合~91 | 日本一级片直播 | 97婷婷五月|