• 简单说说什么是跨域


    发表日期:2019年8月15日


    跨域警告的发生

    如果你做了一些前后端分离的项目,由于此时前端所在的服务地址与后端所在的服务地址不一样,你可能会遇到一个请求被浏览器拦截了的问题,浏览器在检测到当前页面发起的请求不属于当前域就会将其拦截,这是因为浏览器的“同源策略”。


    那么,什么是同源策略呢?
    同源策略用于限制页面发起不同域(源)的请求,用于提高请求的安全性。
    如果两个页面的协议、端口、IP地址(域名)都相同的话,那么这两个页面就是同源,也就是同一个域。

    举例:
    以http://192.168.10.1:8080/index.html为对照源,
    http://192.168.10.1:8080/auth/login.html与它是同源
    http://192.168.10.1:8181/index.html与它不是同源,因为端口不一样;
    http://192.168.10.30:8080/index.html与它不是同源,因为IP地址不一样。



    有些人会问,既然域不一样就会拦截,为什么我用了xxxCDN的css文件,这个请求没有被拦截呢?
    这里要提一些并不是所有的跨域请求都会被拦截的。
    1.通常浏览器不会拦截一些跨域资源嵌入的请求。
    这种所谓的资源嵌入,就是类似于<img>标签中的src,<script>中的src,<link>中的href这样的请求,这样的请求是直接请求资源嵌入到你的页面中的。所以你使用某个cdn的css文件不会被拦截。【所以有种方式就是通过这种嵌入的方式来进行解决跨域的问题】
    2.通常浏览器不会拦截一些跨域写资源的请求。
    这种所谓的跨域写资源,就是所谓的超链接请求,页面重定向,非XMLHttpRequest方式的表单提交(普通的form表单提交)等等。
    3.通常浏览器会拦截跨域读资源的请求。
    XMLHttpRequest提交表单,XMLHttpRequest请求资源,【常见于异步操作】

    除此之外,要再次强调的是,同源策略是浏览器的安全策略。所以如果你直接通过postman这些能够不借助浏览器来发http请求的软件来发请求的话,它是不会拦截你的跨域请求的。



    一个可以用于测试的例子:
    (当我把下面的html文件部署到一个web服务器(tomcat,apache等)中的时候,此时这个网页处于的域应该是我的本机地址localhost:80,此时我根据上述的三个方面来测试浏览器是否拦截。结果是只有最后一个是被拦截的。【不要不部署就直接打开这个页面,否则的话它只是一个普通的本地文件,而非网络文件,此时浏览器不会认为这是一个域】)

    <!DOCTYPE html>
    <html>
    <head>
        <meta charset="UTF-8" />
        <title>用于测试跨域</title>
    
    </head>
    <body>
        <!-- 跨域资源嵌入,允许 -->
        <img src="https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1565884598468&di=bb6ccc7a3280b183e4e13c852bc8353c&imgtype=0&src=http%3A%2F%2Fs04.lmbang.com%2FM00%2FCA%2FDE%2FDpgiA1uPAOKAXmJfAADir1hWa-A750.gif" alt="">
    
        <!-- 跨域资源写操作,允许 -->
        <a href="http://www.baidu.com">百度</a>
    
        <form action="http://www.baidu.com" method="post" >
            用户名:<input type="text" name="username" value="" placeholder="">
            <input type="submit" name="提交" value="提交">
        </form>
    
        <!-- 跨域资源读操作,禁止 -->
        <button onclick="senddata()">XMLHttpRequest请求</button>
    
        <script type="text/javascript">
        var xmlhttp=new XMLHttpRequest();
    
        function senddata(){
            xmlhttp.open("GET","https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1565884598468&di=bb6ccc7a3280b183e4e13c852bc8353c&imgtype=0&src=http%3A%2F%2Fs04.lmbang.com%2FM00%2FCA%2FDE%2FDpgiA1uPAOKAXmJfAADir1hWa-A750.gif",true);
            xmlhttp.send();
        </script>
    </body>
    </html>
    


    补充:

    为什么浏览器会使用同源策略?它想解决什么问题?
    首先,先谈一下cookie吧,cookie主要用于存储一些当前网站的一些数据,在一些旧的web开发中有的还会把用户登录信息存储到cookie中。那么,从安全的角度来考虑的话,你应该希望你的网站的cookie不能被另外一个网站使用(不然cookie中的数据就非常容易被别人窃取了),所以这就引入了域的概念,通过域来限制资源的使用,拦截跨域的资源请求。


    如何允许跨域



    有很多手段来解决跨域,但常见的用于解决跨域调用接口的问题就是CORS

    CORS

    • 如何允许跨域,一种解决方法就是目的域告诉请求者允许什么来源域来请求,那么浏览器就会知道B域是否允许A域发起请求。
    • CORS("跨域资源共享"(Cross-origin resource sharing))就是这样一种解决手段。

    CORS使得浏览器在向目的域发起请求之前先发起一个OPTIONS方式的请求到目的域获取目的域的信息,比如获取目的域允许什么域来请求的信息。

    此时目的域通常需要在响应头中添加以下信息:

    • Access-Control-Allow-Origin:用来声明什么域可以向当前域发起请求。
    • Access-Control-Allow-Methods:用来声明可以向当前域发起什么类型的请求。
    • Access-Control-Max-Age:用来指定本次OPTIONS请求的有效期,单位为秒,在此期间不用发出另一条OPTIONS请求。
    • Access-Control-Allow-Headers:用来允许你附加什么特殊的请求头来发起请求。【有些前后端分离项目会把token放到header中,这时候这个请求头就需要Access-Control-Allow-Headers来声明了】
      【在OPTIONS请求成功后,浏览器会把这些信息记录下来,用来判断发往目的域的请求是否需要拦截。如果OPTIONS请求失败,那么原本要发起的请求就不会发送。】


    但有时候发请求是不会触发OPTIONS请求的。如果这个请求符合以下条件的话:
    1.请求的方式是GET、POST或HEAD。
    2.请求头属于Accept,Accept-Language,Content-Language,Content-Type ,Viewport-Width。
    3.请求头中Content-Type属于application/x-www-form-urlencoded、multipart/form-data、text/plain中的一个。

    这种请求也被称为“简单请求”。



    简单请求的测试:

    下面的例子可以用于测试简单和非简单请求是否会发OPTIONS请求

    <!DOCTYPE html>
    <html>
    <head>
        <meta charset="UTF-8" />
        <title>用于测试简单请求</title>
    </head>
    <body>
        <button onclick="senddata()">XMLHttpRequest请求</button>
        <script type="text/javascript">
        var xmlhttp=new XMLHttpRequest();
    
        function senddata(){
            xmlhttp.open("GET","https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1565884598468&di=bb6ccc7a3280b183e4e13c852bc8353c&imgtype=0&src=http%3A%2F%2Fs04.lmbang.com%2FM00%2FCA%2FDE%2FDpgiA1uPAOKAXmJfAADir1hWa-A750.gif",true);
    xmlhttp.send();
    
            //如果你有一个可以用来测试的接口,可以尝试把这一段注释了,来测试是否发送OPTIONS请求。
      //       xmlhttp.open("POST","http://localhost:8080/hello",true);
             //下面通过加了一个请求头,使得这个请求不是一个不发OPTIONS的请求。
    		// xmlhttp.setRequestHeader("Content-Type", "application/json")
      //       xmlhttp.send();
        }
        </script>
    </body>
    </html>
    


    后端的处理

    下面基于Spring MVC框架来说明后端如何返回返回CORS响应头(注:在spring mvc中,你可以直接使用@CrossOrigin来简单返回CORS响应头。)。
    前端请求测试代码:

    <!DOCTYPE html>
    <html>
    <head>
        <meta charset="UTF-8" />
        <title>用于测试OPTIONS</title>
    </head>
    <body>
        <!-- 跨域资源读操作,禁止 -->
        <button onclick="senddata()">XMLHttpRequest请求</button>
    
        <script type="text/javascript">
        var xmlhttp=new XMLHttpRequest();
    
        function senddata(){
            //如果你有一个可以用来测试的接口,可以尝试把这一段注释了,来测试是否发送OPTIONS请求。
            xmlhttp.open("POST","http://localhost:8080/hello",true);
             //下面通过加了一个请求头,使得这个请求不是一个不发OPTIONS的请求。
    		xmlhttp.setRequestHeader("Content-Type", "application/json")
            xmlhttp.send();
        }
        </script>
    </body>
    </html>
    

    当我们没有部署接口的时候,我们就可以看到,页面是发了一个OPTIONS请求的:

    而且,浏览器控制台也提示以下信息:



    当我们部署了接口的时候,我们先尝试不返回正规的CORS响应头。



    由于没有返回CORS响应头,所以OPTIONS没有请求到合适的CORS信息,所以请求就会被拦截,所以就会报下图的两个警告。



    当我们在响应中添加CORS响应头后,我们可以看到我们刚刚设置的CORS响应头被OPTIONS请求成功了。

    而且请求也被发出去了:



    对于不同编程语言的如何使用CORS,可以自查。



    补充:

    • 除了正常的,例如通过js来处理跨域的。还有一些沙雕的手法,通过修改浏览器来不进行同源策略就是其中一种(治标不治本)。

  • 相关阅读:
    oracle无监听解决方案
    存储过程:期报提示(含有数组)
    分库分表?如何做到永不迁移数据和避免热点?
    存储过程期报提示生成
    Controller层@PathVariable使用
    Java系统架构师学习体系图
    Command line is too long. Shorten command line for xxxxxxxxxxxxxxxxxx
    手动部署oceanbase
    OceanBase Docker 体验
    oceanabse执行计划查看
  • 原文地址:https://www.cnblogs.com/progor/p/11361146.html
Copyright © 2020-2023  润新知