大家都知道spring boot 可以透過@CrossOrigin實現跨域。但是在spring cloud 裡,如果要粒度那麼細的去控制跨域,這個就太繁瑣了,所以一般來說,會在路由zuul裡實現。
在zuul服務下新增一個corsFilter實作跨域,實作起來方便。程式碼如下
@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), 給前端服務允許跨域響應頭,並且不對其進行zuul路由,直接返回成功(200), 給前端服務允許跨域
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,我覺得可能還要想一想……)
按照以上方式配置的話,方法一出現的問題,就得到了解決。服務A能夠正常請求服務B了
雖然是正常實現了需求,但是感覺還是存在很多疑惑,希望大家看到的話,能給我指出不足。一起討論!
相關文章:
跨域解決方案一:使用CORS實作跨域_html/css_WEB-ITnose
#以上是zuul實作Cors跨域的兩種方式:corsFilter、繼承ZuulFilter的詳細內容。更多資訊請關注PHP中文網其他相關文章!