发表日期: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使得浏览器在向目的域发起请求以前先发起一个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,能够自查。