前后端分離,后端用SpringBoot遇到的跨域問題

對于前后端分離的項目來說,如果前端項目與后端項目部署在兩個不同的域下,那么勢必會引起跨域問題的出現。

什么是跨域?

跨域,指的是瀏覽器不能執(zhí)行其他網站的腳本。它是由瀏覽器的同源策略造成的,是瀏覽器施加的安全限制。簡單來說就是不同域名之間相互訪問

同源策略:請求的url地址,必須與瀏覽器上的url地址處于同域上,也就是域名,端口,協議相同.

例如:
http://www.a.com 要訪問 http://www.b.com 這樣域名不同,屬于跨域
http://www.a.com:8081/index.html調用http://www.a.com:8080的接口 這樣端口號不同 也屬于跨域
http://www.a.com:訪問https://www.a.com 這樣也是屬于跨域 協議不一樣

所以在以前前后端不分離的開發(fā)模式中,從來不用關注這個問題,因為所有的請求都是在同一個域內

綜合了網上解決跨域的方式目前有兩種

1.使用JSONP

不過我不太推薦使用,JSONP只支持GET請求,不管是對于前端還是后端來說,寫法與我們平常的ajax寫法不同,同樣后端也需要作出相應的更改。
JSONP的優(yōu)勢在于支持老式瀏覽器,以及可以向不支持CORS的網站請求數據。

2.通過cors協議解決跨域問題

CORS支持所有類型的HTTP請求。
這個也是我非常推薦大家使用的方法
下面我來說下項目中遇到的問題
前端:vue.js
后端:springboot

前端請求接口時,報錯信息如下
Origin 'http://localhost:8080' is therefore not allowed access.
 The credentials mode of an XMLHttpRequest is controlled by the withCredentials attribute

解決方法:
前端請求開啟 withCredentials: true
后臺接口也需要對應的將 Access-Control-Allow-Credentials 設為 true

前端攜帶token傳后端時報錯
Request header field token is not allowed by 
Access-Control-Allow-Headers in preflight response.

這是由于請求頭內并沒有這個名叫token的請求頭字段
默認情況下,只有六種 simple response headers (簡單響應首部)可以暴露給外部:
Cache-Control Content-Language Content-Type Expires Last-Modified Pragma
如果想要讓客戶端可以訪問到其他的首部信息,可以將它們在
Access-Control-Expose-Headers 里面列出來。

所以解決方法是:

Access-Control-Allow-Headers添加自定義的請求頭字段Token
如果還有其他請求頭字段需要使用,也是需要在Access-Control-Allow-Headers添加,不然服務器是不允許的

后面還有一個問題:
Response to preflight request doesn't pass access control check: 
No 'Access-Control-Allow-Origin' header is present on the requested resource. 
Origin 'http://localhost:8080' is therefore not allowed access. 
The response had HTTP status code 503.

這個是請求頭返回服務不可用

解決方式:

我看了下代碼原來是我寫了兩個跨域過濾一個是用寫過濾器Filter,一個是繼承
webmvcconfigureradapter然后我注釋了其中一個就可以了,我想應該是兩個跨域處理器沖突了

拓展

現在開發(fā)客戶端都是發(fā)非簡單請求
非簡單請求是那種對服務器有特殊要求的請求,比如請求方法是PUTDELETE,或者
Content-Type字段的類型是application/json。也就是通過json數據交互
非簡單請求的CORS請求,會在正式通信之前,增加一次HTTP查詢請求,稱為"預檢"請求(preflight)。

流程

瀏覽器先詢問服務器,當前網頁所在的域名是否在服務器的許可名單之中,以及可以使用哪些HTTP動詞和頭信息字段。只有得到肯定答復,瀏覽器才會發(fā)出正式的
XMLHttpRequest請求,否則就報錯。

"預檢"請求用的請求方法是OPTIONS,表示這個請求是用來詢問的。頭信息里面,關鍵字段是Origin,表示請求來自哪個源。
一般不特殊指定源的話都將Access-Control-Allow-Origin設置為*
除了Origin字段,"預檢"請求的頭信息包括兩個特殊字段。
(1)Access-Control-Request-Method
該字段是必須的,用來列出瀏覽器的CORS請求會用到哪些HTTP方法
所使用到的方法也是要在跨域處理器中說明
也就是在Access-Control-Allow-Methods中加入各種請求方法
例如:POST PUT, GET, OPTIONS, DELETE
(2)Access-Control-Request-Headers
該字段是一個逗號分隔的字符串,指定瀏覽器CORS請求會額外發(fā)送的頭信息字段
這個上面有說到,項目需要什么額外的請求頭字段就添加上去
服務器收到"預檢"請求以后,檢查了Origin、Access-Control-Request-Method
Access-Control-Request-Headers字段以后,確認允許跨源請求,就可以做出回應。
說了這么
我分享下我的跨域處理的代碼(我這里是使用過濾器的寫法,也可以使用繼承
webmvcconfigureradapter的方法)

@Component
public class CorsFilter implements Filter {

    /*跨域請求配置*/
    @Override
    public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
        HttpServletResponse response = (HttpServletResponse) res;

        HttpServletRequest reqs = (HttpServletRequest) req;
        response.setHeader("Access-Control-Allow-Origin","*");
        response.setHeader("Access-Control-Allow-Credentials", "true");
        response.setHeader("Access-Control-Allow-Methods", "POST, PUT, GET, OPTIONS, DELETE");
        response.setHeader("Access-Control-Max-Age", "5000");
        response.setHeader("Access-Control-Allow-Headers", "Origin, No-Cache, X-Requested-With, If-Modified-Since, Pragma, Last-Modified, Cache-Control, Expires, Content-Type, X-E4M-With,Authorization,Token");
        chain.doFilter(req, res);
    }
    @Override
    public void init(FilterConfig filterConfig) {}
    @Override
    public void destroy() {}
}

繼承webmvcconfigureradapter的方法

@Configuration
public class WebConfig extends WebMvcConfigurerAdapter {
    @Override
    public void addCorsMappings(CorsRegistry registry) {
        registry.addMapping("/**").allowedOrigins("*").allowedMethods("GET", "POST", "OPTIONS", "PUT")
                .allowedHeaders("Content-Type", "X-Requested-With", "accept", "Origin", "Access-Control-Request-Method",
                        "Access-Control-Request-Headers")
                .exposedHeaders("Access-Control-Allow-Origin", "Access-Control-Allow-Credentials")
                .allowCredentials(true).maxAge(3600);
    }
}

注:以上寫法二選一即可,兩個都寫估計還是會沖突,兩者作用一樣只是寫法不同

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內容

友情鏈接更多精彩內容