No ‘Access-Control-Allow-Origin’ header: 跨域問題踩坑記錄
- 2021 年 11 月 30 日
- 筆記
- springboot
前言
前兩周在伺服器上部署一個系統時,遇到了跨域問題,這也不是第一次遇到跨域問題了,本來以為解決起來會很順利,沒想到解決過程中遇到了很多坑,所以覺得有必要寫一篇部落格記錄一下這個坑。
問題產生原因
本來我們組的應用都有一個統一的網關服務進行介面請求轉發,相關的配置都做好了,並不存在跨域問題。但前兩周因為業務拓展,需要將部分應用拆分出來,部署到其他伺服器上,這裡面就包含我負責的兩個應用。
其中一個應用,因為是前後端不分離的項目,不存在跨域問題,所以部署起來比較順利,直接打了一個jar包丟到伺服器上面就順利跑起來了。另一個項目則是前後端分離的項目,打jar包部署的過程倒時挺順利的,可是當我把前端用nginx跑起來之後,卻看到了類似下圖這樣一堆報錯:
這一看就是跨域問題了。
解決過程
1、修改nginx配置(不起作用)
因為上一次遇到跨域問題,我就是通過nginx配置代理轉發解決的,所以這次我首先想到的還是通過修改nginx配置來解決這個問題,所以我在nginx配置文件中添加了類似這樣的配置:
server{ listen 8888; server_name 192.168.1.100; location /{ proxy_pass //192.168.1.100:8080; } location /api{ proxy_pass //192.168.1.100:8081; // 以api開頭的介面請求,全部轉發到這裡
}
}
然而,並不起作用。之後,我又在nginx配置文件中添加了一些關於請求頭的設置,如下所示:
add_header 'Access-Control-Allow-Headers' '*'; add_header 'Access-Control-Allow-Methods' 'GET,POST,OPTIONS,HEAD,PUT'; add_header 'Access-Control-Allow-Origin' '*'; add_header 'Access-Control-Allow-Credentials' 'true'
同樣的,也沒有解決問題。到這裡,我暫時放棄通過修改nginx配置文件來解決跨域問題的想法了。
2、在後端程式碼中添加跨域處理配置
關於如何在後端解決跨域問題,我之前也了解過,查了一下網上資料後,在程式碼中添加了一下兩個配置類:
WebMvcConfig:
import org.springframework.context.annotation.Configuration; import org.springframework.web.servlet.config.annotation.CorsRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter; /** * 全局處理介面跨域 */ @Configuration public class WebMvcConfig extends WebMvcConfigurerAdapter { @Override public void addCorsMappings(CorsRegistry registry) { registry.addMapping("/**") // .allowedOrigins("*") // 這裡網上的資料大部分說的是.allowedOrigins("*"),但因為我的springboot版本是2.5.5, // allowCredentials為true時並且allowedOrigins不為空且為ALL(這個ALL就是*)時就會拋出異常 // 所以這裡我設置的是.allowedOriginPatterns("*") .allowedOriginPatterns("*") .allowedMethods("POST", "GET", "PUT", "OPTIONS", "DELETE") .maxAge(3600) .allowCredentials(true); } }
CORSFilter:
import org.springframework.stereotype.Component; import javax.servlet.Filter; import javax.servlet.FilterChain; import javax.servlet.FilterConfig; import javax.servlet.ServletException; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; /** * 全局處理介面跨域 */ @Component public class CORSFilter implements Filter { @Override public void init(FilterConfig filterConfig) throws ServletException { Filter.super.init(filterConfig); } @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { HttpServletRequest req = (HttpServletRequest) request; HttpServletResponse res = (HttpServletResponse) response; res.setHeader("Access-Control-Allow-Origin", "*"); res.setHeader("Access-Control-Allow-Credentials", "true"); res.setHeader("Access-Control-Allow-Methods", "GET,HEAD,OPTIONS,POST,PUT"); res.setHeader("Access-Control-Allow-Headers", "*"); chain.doFilter(request, response); } @Override public void destroy() { Filter.super.destroy(); } }
本來以為加上了這兩個配置類,這把絕對沒問題了,可惜,問題依然沒有解決。
但這次部署上去之後,報錯資訊發生了變化,提示某些請求頭(如client_id)沒能成功跨域,這也給了我一些啟發。
3、最終的解決方案
最後的最後,我將CORSFilter配置類中的:
res.setHeader("Access-Control-Allow-Headers", "*");
修改為:
res.setHeader("Access-Control-Allow-Headers", "client_id, Authorization, Access-Control-Allow-Headers, Origin, Accept, X-Requested-With, Content-Type, Access-Control-Request-Method, Access-Control-Request-Headers");
然後,打包部署上去,總算解決了跨域問題。
總結
這次問題解決後,我又查了一些資料,發現了以下兩個知識點:
1、Access-Control-Allow-Origin 請求頭的設置是有一些特殊限制的,當 Access-Control-Allow-Credentials 的值為 true 時會導致Access-Control-Allow-Origin 無法被設置為「*」。
2、因為我的springboot版本較新,所以網上通用的很多跨域配置對我不太適用。比如 Access-Control-Allow-Headers 不能直接設置為 *,而需要將特定的請求頭給明確列出來才可生效。
這次折騰了這麼久,嘗試了網上各種方法都不生效,主要就是因為以上兩點,好在最後總算順利解決了。