• 老被跨域问题烦?看看都有哪些处理方法


    前面写的《IT技术人员的自我修养》,没想到几天内收到了不少良好的反馈,在此感谢大家的关注。往后会不定时分享一些技术、管理领域的工作经验总结与感悟,欢迎大家持续关注、交流。最近被问及一个跨域的问题,包括之前面试时发现很多面试者对跨域及其处理也是一知半解,故本文对该问题进行了梳理总结,以供参考。

    1. 什么是跨域

    理解什么是跨域,就要先了解一个叫“同源策略”的东西,什么是“同源策略”?这是浏览器为了网站访问安全,对来自不同源的请求做一些必要的访问限制的一种策略。那什么叫“同源”呢?我们知道,一个http请求地址一般包含四部分:协议://域名:端口/路径,所谓同源,就是前面三者,即协议、域名、端口都一样。举例说明,假如我们有一个地址 http://blog.jboost.cn/docker-1.html, 来看以下地址是否与它同源

    地址是否同源说明
    https://blog.jboost.cn/docker-1.html 不同源 协议不同,一个http,一个https
    http://www.jboost.cn/docker-1.html 不同源 域名不同
    http://blog.jboost.cn:8080/docker-1.html 不同源 端口不同,一个是默认端口80,一个是8080
    http://blog.jboost.cn/docker-2.html 同源 虽然路径不同,但协议、域名、端口(默认80)都相同

    那么浏览器对不同源的请求做了哪些访问限制呢?共有三种限制

    1. 对Cookie、LocalStorage,以及IndexDB(浏览器提供的类NoSQL的一个本地数据库)的访问

    2. 对DOM的访问

    3. AJAX请求

    而跨域就是要打破这种访问限制,对不同源的资源请求也能顺利进行,最常见的就是AJAX请求,比如前后端分离架构中,两者服务域名不同,前端通过AJAX直接访问服务端接口,就会存在跨域问题。

    2. 为什么会存在跨域

    前面说“同源策略”时已经提到,浏览器是为了网站的访问安全,才设置了跨域这道屏障。那么前面所说的三种限制,分别都是如何来保障网站安全的。

    1. 对本地存储Cookie、LocalStorage、IndexDB的访问限制
      我们系统的登录凭证一般是通过在Cookie中设置 SESSIONID(如针对浏览器表单请求)或直接返回 token(如针对REST请求)的形式返回给客户端的,比如Tomcat是通过在Cookie中设置名为 JSESSIONID 的属性来保存的,而一般REST请求的token前端会存储于 LocalStorage 中,如果不存在访问限制,则你访问的其它网站可能就会获取到这些凭证,然后伪造你的身份来发起非法请求,这就太不安全了。

    2. 对DOM的访问限制
      如果不对DOM进行访问限制,那么其它网站,尤其一些钓鱼网站,就可以通过 <iframe> 的形式拿到你访问网站的DOM,进而获取到你输入的一些敏感信息,比如用户名、密码…

    3. 对AJAX请求的限制
      同源策略规定,AJAX请求只能发给同源的网址,否则就会报错。至于为什么要限制,一方面是避免1中所提到伪造非法请求,另一方面我理解是AJAX过于灵活,如果不做限制,可能网站的接口资源就会被其它网站随意使用,就像你的私有物品被别人招呼都不打任意拿去用一样。 


    总之,同源策略是浏览器提供的最基本的一种安全保障机制或约定。

    3. 怎么实现跨域访问

    我们平常遇到的跨域问题基本都出现在AJAX请求的场景,一般而言,可以通过代理、CORS、JSONP等方式来解决跨域问题。

    3.1 代理

    既然“同源策略”是浏览器端的机制,那我们就可以绕开浏览器,最常见的做法就是使用代理,如 Nginx,比如我们前端项目的域名是 http://blog.jboost.cn,服务端接口域名是 http://api.jboost.cn,我们在 Nginx 中提供如下配置

    server{
        # 端口
        listen 80;
        # 域名
        server_name blog.jboost.cn;
        # 所有 http://blog.jboost.cn/api/xxx 请求都会被转发到 http://api.jboost.cn/api/xxx
        location ^~ /api {
            proxy_pass http://api.jboost.cn;
        }
    }

    则前端通过AJAX请求服务端接口 http://api.jboost.cn/api/xxx 都可以改为通过 http://blog.jboost.cn/api/xxx 来访问,从而避免不同源的跨域问题。 

    3.2 CORS

    CORS是Cross-Origin Resource Sharing的简写,即跨域资源共享,CORS需要服务端与浏览器同时支持,目前所有浏览器(除IE10以下)都支持CORS,因此,实现CORS,主要就是服务端的工作了。例如在Spring Boot中,我们可通过如下配置注册一个CorsFilter的过滤器来实现跨域支持。

     1 @Configuration
     2 @ConditionalOnClass({Servlet.class, CorsFilter.class})
     3 public class CORSAutoConfiguration {
     4 
     5     @Bean
     6 @ConditionalOnMissingBean(name = "corsFilterRegistrationBean")
     7 public FilterRegistrationBean corsFilterRegistrationBean() {
     8         UrlBasedCorsConfigurationSource corsConfigurationSource = new UrlBasedCorsConfigurationSource();
     9 
    10         CorsConfiguration corsConfiguration = new CorsConfiguration();
    11         corsConfiguration.applyPermitDefaultValues();
    12         corsConfiguration.setAllowedMethods(Arrays.asList(CorsConfiguration.ALL));
    13         corsConfiguration.addExposedHeader(HttpHeaders.DATE);
    14 
    15         corsConfigurationSource.registerCorsConfiguration("/**", corsConfiguration);
    16 
    17         CorsFilter corsFilter = new CorsFilter(corsConfigurationSource);
    18         FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean();
    19         filterRegistrationBean.setFilter(corsFilter);
    20         filterRegistrationBean.setOrder(Ordered.HIGHEST_PRECEDENCE);
    21         filterRegistrationBean.addUrlPatterns("/*");
    22 
    23         return filterRegistrationBean;
    24     }
    25 }

    其实质就是在响应消息的Header中添加几个属性,主要有 

    • Access-Control-Allow-Origin 必需,表示允许跨域的请求源,可以是具体的域名,也可以是 * ,表示任意域名

    • Access-Control-Allow-Methods 必需,表示允许跨域访问的HTTP方法,如GET、POST、PUT、DELETE等,可以是 * ,表示所有

    • Access-Control-Allow-Headers 如果请求包括 Access-Control-Request-Headers 头信息,则必需,表示服务器支持的所有头信息字段

    3.3 JSONP

    JSONP是利用浏览器对HTML一些标签(如 <script><img>等)的 src 属性不具有同源策略限制的特性实现的,如前端添加

    <script type="text/javascript" src="http://api.jboost.cn/hello?name=jboost&callback=jsonpCallback"/>

    并且定义JS方法 jsonpCallback。服务端接口返回内容需要是JS方法jsonpCallback的调用格式,如jsonpCallback({"name":"jboost"}),这样在jsonpCallback方法中就可以获取服务端实际返回的结果数据{"name":"jboost"}了。
    JSONP方式的局限性也很明显,一是只支持GET请求——你没见过哪些<script><img>标签是POST请求吧,二是需要对服务端返回数据格式做处理。

    4. 总结

    三种跨域支持的实现,代理方式最简单,对客户端、服务端都不具有侵入性,但如果需要支持的请求源比较多,或者是与第三方对接的话,代理方式就不太适用了。CORS相对来说是一种标准的处理方式,并且通过过滤器的方式对业务代码也没有任何侵入性。而JSONP方式局限性较大,只支持GET,并且需要服务端做返回数据格式的支持。可针对具体情况选择适用的方式。

     

  • 相关阅读:
    activeMQ
    读写xml
    PLSQL
    oracle语法
    cxf远程调用服务
    FastDFS在linux下的安装和整合nginx实现上传图片和url访问
    dubbo和zookeeper的应用
    solr和Lucene的配置方式和应用
    win10 下安装 MongoDB 数据库支持模块(python)
    nodeJs 对 Mysql 数据库的 curd
  • 原文地址:https://www.cnblogs.com/spec-dog/p/11277943.html
Copyright © 2020-2023  润新知