实践[前后端]解决实际开发生产中跨域的几种前后端处理方式

145 阅读4分钟

http 的官方定义: datatracker.ietf.org/doc/html/rf…

跨域产生的问题

  1. 无法通过dom获取跨域的图片资源
  2. 无法通过传统的XMLHttpRequest\fecth进行ajax(异步请求刷新技术)请求的正常访问
  3. 无法操作非同源的内嵌页面
  4. 无法共享cookie,localstorage,sessionstorage,indexdb等存储资源
No "Access-Control-Allow-Origin" header is persent on the reqested resource.
请求的资源上没有“Access-Control-Allow-Origin”头.

导致跨域的原因

  1. 跨域问题单纯是浏览器和服务器之间的约定,在早期浏览器设计的时候,为了保证服务器和浏览器的资源获取是安全可靠的,就引入了同源策略,即域名、端口号、协议三者统一,才可以正常访问。
  2. 浏览器和服务器之间的跨域协议头没有设置正确。

跨域的解决方案

跨域是浏览器自己的安全策略问题,也就是说该问题的核心是浏览器,我们只需要通过浏览器的安全策略即可,由此产生如下几种解决办法

  1. 通过代理解决,如使用nginx,webpack-dev-server等实现请求的反向代理,使请求统一通过代理进行访问
  2. 通过服务端设置参数解决,通过在请求头上添加
    1. access-control-allow-origin:指定允许访问资源的url即对应的外部服务器地址
    2. access-control-allow-method:指定允许的访问方式,包括get,post,option,delete,put
  3. 通过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手段罢了。