前提提示:最近的項(xiàng)目中,采用了前后端分離的開發(fā)模式。前端通過fetch api訪問后端rest api谢肾。其間很自然的就會(huì)出現(xiàn)跨域問題。
什么是跨域钦铺?
以下是腦補(bǔ)之后的一些理解订雾。
CORS(Cross-origin request sharing)跨域資源共享,是一個(gè)w3c標(biāo)準(zhǔn)矛洞,它允許你向一個(gè)不同源服務(wù)發(fā)出XMLHttpRequest請(qǐng)求洼哎,從而克服ajax只能請(qǐng)求同源服務(wù)的限制。
瀏覽器出于安全考慮沼本,實(shí)施的一種同源策略,對(duì)非同源訪問會(huì)主動(dòng)攔截掉噩峦。
同源是指具有相同的:url協(xié)議(eg:http,https)、域名(eg:google.com)抽兆、端口
在瀏覽器中识补,script、img郊丛、iframe李请、link(這些標(biāo)簽似乎都有src屬性)等標(biāo)簽可以加載跨域資源,而不受同源限制厉熟。
那么如何處理跨域的請(qǐng)求呢导盅?目前比較多做法是jsonp,nginx反向代理揍瑟,cors本文主要介紹cors.
jsonp:通過可以跨域的script標(biāo)簽白翻,這樣請(qǐng)求就能跨域訪問了,通過約定好回調(diào)绢片,就能獲得相關(guān)響應(yīng)數(shù)據(jù)滤馍。缺點(diǎn)是只支持GET不支持POST等其它類型請(qǐng)求;
nginx代理:通過一個(gè)代理服務(wù)器,轉(zhuǎn)發(fā)請(qǐng)求到后端服務(wù)底循。缺點(diǎn)是擴(kuò)展性不好巢株。開發(fā),測(cè)試,預(yù)發(fā)布等幾個(gè)環(huán)境切換都需要配置。
cors: 支持所有類型的http請(qǐng)求熙涤,在服務(wù)端設(shè)置即可阁苞。缺點(diǎn)是老的瀏覽器不兼容(IE6,7,8 沒關(guān)系,這些老版本不要管了)
CORS基本過程:
在HttpServertResponse響應(yīng)中添加響應(yīng)頭:
Access-Control-Allow-Origin:(必設(shè))添加瀏覽器允許的域名,可設(shè)置*表示任何祠挫。(origin是你瀏覽器上的地址組成的)
Access-Control-Allow-Credentials:(可設(shè))是否允許發(fā)送cookie那槽。如果設(shè)置為true,客戶端請(qǐng)求也要將withCredentials設(shè)為true;
Access-Control-Request-Method: 必須的,表示CORS上會(huì)使用到那些HTTP方法;
Access-Control-Request-Headers: 必須的,表示CORS上會(huì)有那些額外的的有信息;
SpringBoot支持cors
SpringBoot中已經(jīng)很好的支持了cors
CorsFilter類提供了改功能,通過構(gòu)造UrlBasedCorsConfigurationSource類相關(guān)配置
以下是基本的代碼:
@Configuration
public classCorsConfig {
private CorsConfiguration buildConfig(){
CorsConfiguration corsConfiguration =newCorsConfiguration();
corsConfiguration.addAllowedOrigin("*");//允許任何域名
corsConfiguration.addAllowedHeader("*");//允許任何頭
corsConfiguration.addAllowedMethod("*");//允許任何方法get,post..
returncorsConfiguration;
}
@Bean
public CorsFiltercors Filter(){
UrlBasedCorsConfigurationSource source =new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**",buildConfig());
return newCorsFilter(source);
}}
至此等舔,一個(gè)接受任何域的跨域請(qǐng)求就ok了骚灸。
跨域共享cookie
但是還未結(jié)束,對(duì)于權(quán)限驗(yàn)證慌植,目前還是使用的基于session,cookie的驗(yàn)證甚牲,所以這種方案需要實(shí)現(xiàn)跨域共享cookie.(強(qiáng)烈不建議使用cookie)义郑,那么對(duì)于客戶端的請(qǐng)求就需要額外加上幾個(gè)參數(shù)。
對(duì)于跨域共享cookie丈钙,需要注意的是后端響應(yīng)的response head里的 Access-Control-Allow-Origin不能是通配符魔慷,且不能配置多個(gè),Access-Control-Allow-Credentials需要設(shè)置為true
對(duì)于Access-Control-Allow-Origin的限制著恩,只能指定單一域名或使用req.getHeader("Origin")來獲取每次請(qǐng)求的Origin院尔。這樣一來,你要另外寫一個(gè)Filter喉誊。
客戶端的設(shè)置:以jQuery為例需要在里面加入xhrFields: {withCredentials: true},crossDomain: true?
在這些修改做好后邀摆,訪問后端接口,但是依然不能解決伍茄。排查了半天栋盹,是跨域請(qǐng)求之前都要執(zhí)行一個(gè)OPTIONS請(qǐng)求,這個(gè)請(qǐng)求是一個(gè)預(yù)檢測(cè)的服務(wù)敷矫,用于獲知服務(wù)端能接受哪些請(qǐng)求方法例获,origin等信息。后端會(huì)在響應(yīng)中返回一個(gè)cookie曹仗,在下一次請(qǐng)求時(shí)會(huì)帶上這個(gè)cookie榨汤。就實(shí)現(xiàn)了基本的跨域cookie共享,后端也能正常獲取到session怎茫。
這個(gè)請(qǐng)求可以特殊處理收壕。