http 的官方定义: datatracker.ietf.org/doc/html/rf…
跨域产生的问题
- 无法通过dom获取跨域的图片资源
- 无法通过传统的XMLHttpRequest\fecth进行ajax(异步请求刷新技术)请求的正常访问
- 无法操作非同源的内嵌页面
- 无法共享cookie,localstorage,sessionstorage,indexdb等存储资源
No "Access-Control-Allow-Origin" header is persent on the reqested resource.
请求的资源上没有“Access-Control-Allow-Origin”头.
导致跨域的原因
- 跨域问题单纯是浏览器和服务器之间的约定,在早期浏览器设计的时候,为了保证服务器和浏览器的资源获取是安全可靠的,就引入了同源策略,即域名、端口号、协议三者统一,才可以正常访问。
- 浏览器和服务器之间的跨域协议头没有设置正确。
跨域的解决方案
跨域是浏览器自己的安全策略问题,也就是说该问题的核心是浏览器,我们只需要通过浏览器的安全策略即可,由此产生如下几种解决办法
- 通过代理解决,如使用nginx,webpack-dev-server等实现请求的反向代理,使请求统一通过代理进行访问
- 通过服务端设置参数解决,通过在请求头上添加
- access-control-allow-origin:指定允许访问资源的url即对应的外部服务器地址
- access-control-allow-method:指定允许的访问方式,包括get,post,option,delete,put
- 通过jsonp方式进行访问,但是随着时代的发展该方式已经不是主流的解决方案了。
跨域解决方案的实现方法
通过代理进行跨域的解决,本质上代理服务器就是一个web服务,它替我们完成了请求的转发和接收,所以为了区分具体的代理路径,我们需要对不同服务的代理地址设置一个基础路径,表示当匹配到基础路径时我才需要进行代理。
webpack-dev-server配置
{
devServer: {
proxy:{
"/xxx": {
target:"xxx.xxx.xxx/xxx", // 完整的代理地址
changeOrigin: true, // 开启跨域
pathRewrite: { // 代理地址转换策略
"^/xxx": ""
}
}
}
}
}
nginx配置文件
{
location /xxx {
proxy_pass http://xxx:xxx/xxx;
}
}
服务器修改请求头
服务端可以设置的跨域请求有
Access-Control-Allow-Credentials 允许客户端向跨域服务器发送认证信息
Access-Control-Allow-Headers 允许服务器指定哪些自定义请求头可以被浏览器访问
Access-Control-Allow-Methods 允许客户端向服务端请求的方法或方法列表
Access-Control-Allow-Origin 允许被指定的域名访问
Access-Control-Expose-Headers 允许客户端访问请求头的信息
Access-Control-Max-Age 允许跨域接口的最大生存时间,在生存时间内无需额外进行跨域验证
Access-Control-Request-Headers 允许服务器指定哪些自定义请求头可以被浏览器访问
Access-Control-Request-Method 告诉服务器实际请求所使用的HTTP 方法,以便服务器判断是否允许此次跨域请求
通过实现mvc配置接口
@Configuration
public class CorsConfiguration implements WebMvcConfigurer {
@Override
public void addCorsMappings (CorsRegistry registry) {
registry.addMapping("/**") // 允许跨域的资源路径
.allowedOriginPatterns("*") // 允许的其它服务器地址
.allowCredentials(true) // 客户端向跨域服务器发送认证信息,如cookie等
.allowedMethods("*") // 允许跨域的方法
.maxAge(6000); // 请求跨域的时间
}
}
通过@CorssOrigin注解
@CorssOrigin(origins = "url")
@RestController
@RequestMapping("/")
public class TestController {}
通过拦截器实现
@Configuration
public class FilterCors {
@Bean
public FilterRegistrationBean corsFilter (){
// 创建一个用于注册配置信息的实例
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
// 创建配置项
CorsConfiguration config = new CorsConfiguration();
config.setAllowCredentials(true);
config.addAllowedOrigin("*");
config.addAllowedHeader("*");
config.addAllowedMethod("*");
source.registerCorsConfiguration("/**", config);
// 获取过滤器bean实例
FilterRegistrationBean bean = new FilterRegistrationBean(new CorsFilter(source));
bean.setOrder(0); // 过滤器优先级
return bean;
}
}
通过实现filter过滤器实现请求头跨域处理
@Configuration
class CorsFilter2 implements Filter {
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
HttpServletResponse res =(HttpServletResponse) servletResponse;
res.addHeader("Access-Control-Allow-Credentials",
"true");
res.addHeader("Access-Control-Allow-Origin",
"*");
res.addHeader("Access-Control-Allow-Methods",
"GET, POST, DELETE, PUT");
res.addHeader("Access-Control-Allow-Headers",
"Content-Type,X-CAF-Authorization-Token,sessionToken,X-TOKEN");
filterChain.doFilter(servletRequest,servletResponse);
}
@Override
public void init(FilterConfig filterConfig) throws ServletException {
Filter.super.init(filterConfig);
}
@Override
public void destroy() {
Filter.super.destroy();
}
}
总结
解决跨域的根本方式还是在服务器与浏览器的安全策略协议,我们需要对CORS参数进行设置,其它方式都是hack手段罢了。