大家都知道spring boot 可以通过@CrossOrigin实现跨域。但是在spring cloud 里,如果要粒度那么细的去控制跨域,这个就太繁琐了,所以一般来说,会在路由zuul里实现。
第一种方式:corsFilter
在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请求跨域失败。(所以如果大家发现我哪里写的不对,请务必通知我,让我也明白为什么失败!谢谢!)
第二种方式:继承ZuulFilter
因为第一种方式在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), 给前端服务允许跨域
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了
虽然是正常实现了需求,但是感觉还是存在很多疑惑,希望大家看到的话,能给我指出不足。一起讨论!