大背景:前后端分離后信卡,項目分開部署,域名不一致幔妨,ajax請求時需要解決跨域問題抄瓦。
服務(wù)端支持跨域方案:
1、spring早已經(jīng)支持跨域的配置陶冷。
2钙姊、@CrossOrgin注解方式,支持配置到controller或者具體的方法上埂伦。不多解釋注解的參數(shù)煞额。
說明:我理解的是,1和2方案都是通過spring提供的CorsFilter做了攔截沾谜。
3膊毁、spring 攔截器方式?
public classCrossDomainInterceptorextendsHandlerInterceptorAdapter {
@Override
???public booleanpreHandle(HttpServletRequest request,HttpServletResponse response,Object handler)throwsException {
if(RequestMethod.OPTIONS.name().equalsIgnoreCase(request.getMethod())) {
response.addHeader("Access-Control-Allow-Origin",request.getHeader("Origin"));
???????????response.addHeader("Access-Control-Allow-Methods","GET,POST,PUT,OPTIONS,DELETE");
???????????response.addHeader("Access-Control-Max-Age","1800");
???????????response.addHeader("Access-Control-Allow-Headers","Content-Type,x-requested-with,access-token");
???????????response.addHeader("Access-Control-Allow-Credentials","true");
? ? ? ? ? ? //response.setStatus(HttpStatus.SC_OK);
??????????? return false;
???????}
return true;
???}
}
4、自定義Filter基跑,然后在XML配置 filter婚温。
public classCrossDomainFilterimplementsFilter{
@Override
???public voidinit(FilterConfig filterConfig)throwsServletException {
??? }
@Override
???public voiddoFilter(ServletRequest servletRequest,ServletResponse servletResponse,FilterChain filterChain)throwsIOException,ServletException {
if(servletRequestinstanceofHttpServletRequest && servletResponseinstanceofHttpServletResponse) {
HttpServletRequest request = (HttpServletRequest) servletRequest;
???????????HttpServletResponse response = (HttpServletResponse) servletResponse;
??????????? if(CorsUtils.isCorsRequest(request)) {
log.info(request.getRequestURL().toString() +" options request into CrossDomainFilter");
??????????????? if(!processRequest(request,response) || CorsUtils.isPreFlightRequest(request)) {
return;
???????????????}
??????????? }
filterChain.doFilter(request,response);
???????}else{
filterChain.doFilter(servletRequest,servletResponse);
???????}
??? }
private booleanprocessRequest(HttpServletRequest request,HttpServletResponse response) {
booleanisValid =false;
???????String curOrigin = request.getHeader("Origin");
???????String[] domains ="http://localhost:8080,http://localhost:8090".split(",");
??????? if(domains.length>0) {
for(String domain : domains) {
if(curOrigin.equals(domain)) {
response.addHeader("Access-Control-Allow-Origin",domain);
???????????????????response.addHeader("Access-Control-Allow-Methods","GET,POST,PUT,OPTIONS,DELETE");
???????????????????response.addHeader("Access-Control-Allow-Headers","Content-Type, x-requested-with, access-token");
???????????????????response.addHeader("Access-Control-Max-Age","1800");
???????????????????response.addHeader("Access-Control-Allow-Credentials","true");
???????????????????isValid =true;
??????????????????? break;
???????????????}
??????????? }
??????? }
returnisValid;
???}
@Override
???public voiddestroy() {
??? }
}
以上四種方案好像所以的看上去都是那么簡單,沒什么難度媳否。我一開始也是這樣認(rèn)為的栅螟。然而。篱竭。力图。。
過程中遇到如下幾個問題掺逼。
1)我們希望domain可支持的域名是可以動態(tài)配置吃媒,通過公司的配置系統(tǒng)可以動態(tài)獲取allowOrigins。那么方案1被我拋棄了吕喘,因為xml里配置無法動態(tài)生效赘那。因為是基于全局配置,就沒有考慮方案2.
2)我用了spring攔截器方式CrossDomainInterceptor氯质,或者CrossDomainFilter extends OncePerRequestFilter募舟。就是這個過程都被spring管理著。但是出現(xiàn)了狀況:
A病梢、請求依然403. 原因:經(jīng)過各種嘗試發(fā)現(xiàn)胃珍,Access-Control-Allow-Credentials梁肿、Access-Control-Allow-Headers有設(shè)置時,Access-Control-Allow-Origin 不要設(shè)置成 *觅彰。并且Access-Control-Allow-Headers 需要和前端傳上來的header 參數(shù)名匹配上吩蔑,否則很容易403.
B、解決了測試環(huán)境的403之后填抬,有同學(xué)又反應(yīng)本地開發(fā)環(huán)境出現(xiàn)“Invalid Cors Request”烛芬。找了4個同學(xué)本地postman請求服務(wù)端接口,2個同學(xué)反應(yīng)沒有問題飒责,2個同學(xué)反應(yīng)出現(xiàn)“Invalid Cors Request”赘娄。這里很難理解,postman里的請求就是簡單的接口測試宏蛉,不存在跨域問題遣臼,無法理解。解決方案:在網(wǎng)上有看到帖子說是因為多個filter順序問題拾并,其他filter導(dǎo)致了跨域filter出現(xiàn)了問題揍堰。。嗅义。(確實項目還集成了其他需要用到過濾器的地方屏歹,如:安全框架接入)
基于以上出現(xiàn)的狀況,我決定從頭開始之碗,回歸本質(zhì)蝙眶。首先跨域就是需要對瀏覽器PreflightRequest(Options預(yù)檢查請求)。進(jìn)行過濾褪那。那么就定義一個java filter吧幽纷。filter不再繼承OncePerRequestFilter(畢竟讓spring控制后自己就不會關(guān)注那么多了,這未必是好事)武通。然后把filter配置到web.xml(可以決定filter的順序霹崎,也為了解決以上最后提到的filter順序問題)珊搀。代碼參考方案4冶忱。結(jié)果嘛,自然是一切就那么好起來了境析。
Access-Control-Allow-Headers 需要支持前端傳上來的參數(shù)囚枪。
Access-Control-Allow-Credentials、Access-Control-Allow-Headers有設(shè)置時劳淆,Access-Control-Allow-Origin 不要設(shè)置成 *链沼,只能支持一個。
注意過濾器的順序問題沛鸵。(即括勺,你需要先做哪一步過濾)
引用一些參考的文章:
https://blog.csdn.net/dalangzhonghangxing/article/details/52911230