你們都知道spring boot 能夠經過@CrossOrigin實現跨域。可是在spring cloud 裏,若是要粒度那麼細的去控制跨域,這個就太繁瑣了,因此通常來講,會在路由zuul裏實現。前端
在zuul服務下添加一個corsFilter實現跨域,實現起來方便。代碼以下spring
@Configuration public class GateWayCorsConfig { @Bean public FilterRegistrationBean corsFilter() { final UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); final CorsConfiguration config = new CorsConfiguration(); config.setAllowCredentials(true); config.addAllowedOrigin("*"); config.addAllowedHeader("*"); config.addAllowedMethod("*"); //這個請求頭在https中會出現,可是有點問題,下面我會說 //config.addExposedHeader("X-forwared-port, X-forwarded-host"); source.registerCorsConfiguration("/**", config); FilterRegistrationBean bean = new FilterRegistrationBean(new CorsFilter(source)); bean.setOrder(Ordered.HIGHEST_PRECEDENCE); return bean; } }
通過測試,這樣的配置在http的狀況下跨域是OK的,可是當個人環境切換的https的狀況下就發生了奇怪的問題。說明一下我遇到的問題。
前端 服務A 和 後端服務B 在同一臺服務器上,服務A 調用 服務B 時,服務A經過負載均衡進入服務B時:
http時,服務A的請求跨域成功,https時,服務A的請求跨域失敗。
也就是端口爲443的時候,會被認爲跨域失敗!!
我一開始對比了請求頭,覺得是少了ExposedHeader的"X-forwared-port, X-forwarded-host",可是添加後,仍是失敗。由於急着上線,因此我沒有去深刻測試到底什麼緣由引發的https請求跨域失敗。(因此若是你們發現我哪裏寫的不對,請務必通知我,讓我也明白爲何失敗!謝謝!)後端
由於第一種方式在https下失敗後,我嘗試了用zuulfilter實現cors的方式
一共須要兩個filiter:一個pre, 一個post跨域
Pre-Filter:服務器
@Component public class FirstFilter extends ZuulFilter { private Logger logger = LoggerFactory.getLogger(FirstFilter.class); @Override public String filterType() { /* pre:能夠在請求被路由以前調用 route:在路由請求時候被調用 post:在route和error過濾器以後被調用 error:處理請求時發生錯誤時被調用 * */ // 前置過濾器 return FilterConstants.PRE_TYPE; } @Override public int filterOrder() { //// 優先級爲0,數字越大,優先級越低 return 0; } @Override public boolean shouldFilter() { RequestContext ctx = RequestContext.getCurrentContext(); HttpServletRequest request = ctx.getRequest(); //只過濾OPTIONS 請求 if(request.getMethod().equals(RequestMethod.OPTIONS.name())){ return true; } return false; } @Override public Object run() { logger.debug("*****************FirstFilter run start*****************"); RequestContext ctx = RequestContext.getCurrentContext(); HttpServletResponse response = ctx.getResponse(); HttpServletRequest request = ctx.getRequest(); response.setHeader("Access-Control-Allow-Origin",request.getHeader("Origin")); response.setHeader("Access-Control-Allow-Credentials","true"); response.setHeader("Access-Control-Allow-Headers","authorization, content-type"); response.setHeader("Access-Control-Allow-Methods","POST,GET"); response.setHeader("Access-Control-Expose-Headers","X-forwared-port, X-forwarded-host"); response.setHeader("Vary","Origin,Access-Control-Request-Method,Access-Control-Request-Headers"); //再也不路由 ctx.setSendZuulResponse(false); ctx.setResponseStatusCode(200); logger.debug("*****************FirstFilter run end*****************"); return null; } }
Pre-Filter 用來處理預處理OPTIONS請求,當發現是OPTIONS請求的時候,給出跨域響應頭,而且不對其進行zuul路由,直接返回成功(200), 給前端服務容許跨域cors
post-Filter :負載均衡
@Component public class PostFilter extends ZuulFilter { private Logger logger = LoggerFactory.getLogger(PostFilter.class); @Override public String filterType() { /* pre:能夠在請求被路由以前調用 route:在路由請求時候被調用 post:在route和error過濾器以後被調用 error:處理請求時發生錯誤時被調用 * */ // 前置過濾器 return FilterConstants.POST_TYPE; } @Override public int filterOrder() { //// 優先級爲0,數字越大,優先級越低 return 2; } @Override public boolean shouldFilter() { RequestContext ctx = RequestContext.getCurrentContext(); HttpServletRequest request = ctx.getRequest(); //過濾各類POST請求 if(request.getMethod().equals(RequestMethod.OPTIONS.name())){ return false; } return true; } @Override public Object run() { logger.debug("*****************PostFilter run start*****************"); RequestContext ctx = RequestContext.getCurrentContext(); HttpServletResponse response = ctx.getResponse(); HttpServletRequest request = ctx.getRequest(); response.setHeader("Access-Control-Allow-Origin",request.getHeader("Origin")); response.setHeader("Access-Control-Allow-Credentials","true"); response.setHeader("Access-Control-Expose-Headers","X-forwared-port, X-forwarded-host"); response.setHeader("Vary","Origin,Access-Control-Request-Method,Access-Control-Request-Headers"); //容許繼續路由 ctx.setSendZuulResponse(true); ctx.setResponseStatusCode(200); logger.debug("*****************PostFilter run end*****************"); return null; } }
Post-Filter 用來處理 預處理OPTIONS之外的請求,對於正常的請求,不但要給出跨域請求頭,還須要容許請求進行路由(不然你的請求到這兒就結束啦),而後返回狀態碼200。(emmmm……這裏要不要返回200,我以爲可能還要想想……)ide
按照以上方式配置的話,方法一出現的問題,就獲得瞭解決。服務A可以正常請求服務B了post
雖然是正常實現了需求,可是感受仍是存在不少疑惑,但願你們看到的話,能給我指出不足。一塊兒討論!測試