微服务架构最近几年风生水起,带来好处的同时,其自身也带来了不少问题,比如前后端交互导致的跨域问题。
为了安全,浏览器提供了同源策略的安全机制,来防止浏览器受到XSS、CSRF等攻击,所以导致不同域名之间无法互相访问。所谓同源是指"协议+域名+端口"三者相同,即便两个不同的域名指向同一个ip地址,也非同源。
大家可以先阅读一下“九种跨域方式实现原理”,该文对跨域相关知识进行了详细的讲解。在该文中提到了跨域请求的本质:
请求跨域了,那么请求到底发出去没有??
跨域并不是请求发不出去,请求能发出去,服务端能收到请求并正常返回结果,只是结果被浏览器拦截了。
上述表达说明了我们只需要在请求响应返回的时候告诉浏览器可以跨域访问就可以了。
本文基于springcloud使用CORS (Cross-Origin Resource Sharing跨域资源共享技术)通过服务端实现跨域。
1. 定义一个过滤类型为POST的过滤器,该过滤器会在处理完具体的服务后,在即将返回响应时对response进行处理(本方式是目前项目中实际用到的)推荐
package ex;
import javax.servlet.http.HttpServletResponse;
import org.springframework.stereotype.Component;
import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.context.RequestContext;
@Component
public class CorsResponseFilter extends ZuulFilter {
/**
*返回布尔值来判断该过滤器是否要执行。可以通过此方法来执行过滤器的有效范围
*/
@Override
public boolean shouldFilter() {
return true;
}
/**
* 具体逻辑
*/
@Override
public Object run() {
RequestContext ctx = RequestContext.getCurrentContext();
HttpServletResponse response = ctx.getResponse();
// 设置哪个源可以访问我
response.setHeader("Access-Control-Allow-Origin", "*");
// 允许哪个方法(也就是哪种类型的请求)访问我
response.setHeader("Access-Control-Allow-Methods", "PUT,GET,POST,OPTIONS");
// 允许携带哪个头访问我
response.setHeader("Access-Control-Allow-Headers", "X-Requested-With, Content-Type, X-File-Name,token");
//文本大小,可以不设置(下载文件的时候注意下,发现没加的话,文件大小变了)
if (ctx.getOriginContentLength() != null && ctx.getOriginContentLength() > 0) {
response.setHeader("Content-Length", ctx.getOriginContentLength().toString());
}
//允许携带cookie
response.setHeader("Access-Control-Allow-Credentials", "true");
ctx.setResponse(response);
return null;
}
/**
* 过滤器类型:
* pre: 在请求被路由之前调用
* route: 在路由请求时被调用
* post: 表示在route和error过滤器之后被调用
* error: 处理请求发生错误是被调用
*/
@Override
public String filterType() {
return "post";
}
/**
* 过滤器执行顺序,数值越小优先级越高,不同类型的过滤器,执行顺序的值可以相同
*/
@Override
public int filterOrder() {
return 1000;
}
}
2. 使用CorsFilter提供配置类(此种方式没做验证,大家自己测测吧)
@Configuration
public class CorsConfig {
public CorsFilter corsFilter() {
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
CorsConfiguration config = new CorsConfiguration();
config.setAllowCredentials(true);
config.addAllowedOrigin("*");
config.addAllowedHeader("*");
config.addAllowedMethod("OPTIONS");
config.addAllowedMethod("HEAD");
config.addAllowedMethod("GET");
config.addAllowedMethod("PUT");
config.addAllowedMethod("POST");
config.addAllowedMethod("DELETE");
config.addAllowedMethod("PATCH");
source.registerCorsConfiguration("/**", config);
return new CorsFilter(source);
}
}
1. 上面网关部分提到的第2中方式(全局跨域)
2. 覆写WebMvcConfigurer的addCorsMappings方法(全局跨域)
@Configuration
public class CorsConfig implements WebMvcConfigurer {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
//是否发送Cookie
.allowCredentials(true)
//放行哪些原始域
.allowedOrigins("*")
.allowedMethods(new String[]{"GET", "POST", "PUT", "DELETE"})
.allowedHeaders("*")
.exposedHeaders("*");
}
}
3. 在类或方法上使用跨域注解@CrossOrigin
在类上
@RestController
@CrossOrigin(origins = "*")
public class CorsController {
@RequestMapping("/cors")
public String cors() {
return "跨域了。。。。。。";
}
}
在方法上
@RestController
public class CorsController {
@CrossOrigin(origins = "*")
@RequestMapping("/cors")
public String cors() {
return "跨域了。。。。。。";
}
}
如果项目用到网关的话,还是建议在网关中统一进行跨域处理,这样处理可以使服务提供方只专注业务处理就可以了,不用每个微服务都要自己进行跨域处理。
不要网关、服务都同时进行跨域处理,否则有问题