简单说说什么是跨域

发表日期:2019年8月15日css


跨域警告的发生

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


那么,什么是同源策略呢?
同源策略用于限制页面发起不一样域(源)的请求,用于提升请求的安全性。
若是两个页面的协议、端口、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地址不同。java




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

除此以外,要再次强调的是,同源策略是浏览器的安全策略。因此若是你直接经过postman这些可以不借助浏览器来发http请求的软件来发请求的话,它是不会拦截你的跨域请求的。spring




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

<!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中的数据就很是容易被别人窃取了),因此这就引入了域的概念,经过域来限制资源的使用,拦截跨域的资源请求。编程


如何容许跨域




有不少手段来解决跨域,但常见的用于解决跨域调用接口的问题就是CORSjson

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来处理跨域的。还有一些沙雕的手法,经过修改浏览器来不进行同源策略就是其中一种(治标不治本)。