作者简介:大家好,我是smart哥,前中兴通讯、美团架构师,现某互联网公司CTO
联系qq:184480602,加我进群,大家一起学习,一起进步,一起对抗互联网寒冬
不论个人练习还是实际开发,我们都不可避免地会遇到跨域问题,而造成跨域的罪魁祸首就是浏览器的同源策略。要解决跨域,首先要了解同源策略。
主要内容
百度“同源策略”得到以下回答:
同源策略,它是由Netscape提出的一个著名的安全策略。所有支持JavaScript 的浏览器都会使用这个策略。
所谓同源是指,域名,协议,端口相同。
在一个浏览器的两个tab页中分别打开百度和谷歌的页面,当浏览器的百度tab页执行一个脚本的时候会检查这个脚本是属于哪个页面的,即检查是否同源,只有和百度同源的脚本才会被执行。如果非同源,那么在请求数据时,浏览器会在控制台中报一个异常,提示拒绝访问。
同源策略是浏览器的行为,是浏览器为了保护本地数据不被JavaScript代码获取回来的数据污染而做出的拦截行为。即请求发送了,服务器响应了,但是无法被浏览器接收。
简单来说,就是:
很多人以为同源策略是浏览器不让请求发出去、或者后端拒绝返回数据。NO!实际情况是,请求正常发出,后端接口也正常响应,只不过数据到了浏览器后被丢弃了。
同源策略限制内容有:
以下情况都属于跨域:
跨域原因说明 |
示例 |
域名不同 |
与 |
域名相同,端口不同 |
与 |
二级域名不同 |
与 |
协议不同 |
HTTP与HTTPS |
简单来说,是否跨域的3个因素为:协议、域名、端口。
需要注意,如果协议、域名、端口都相同,但是请求路径不同,不属于跨域,如:
https://www.jd.com/item
https://www.jd.com/goods
而上面示意图中,在manage.leyou.com
的页面访问api.leyou.com
的接口,由于二级域名不同,导致跨域。从这个角度来看,只要是前后端分离的项目,必然跨域!(即使部署在同一个服务器,前后端项目端口肯定不同)
随手建一个SpringBoot项目后,把下面的文件拷过去
目录结构
avatar.png是头像,你们随便用啥。
UserController
@RestController
public class UserController {
@GetMapping(value = "/getUser/{id}")
public User getUser(@PathVariable("id") Long id) {
// id没用上,就是演示一下@PathVariable注解
System.out.println("id:" + id);
User user = new User();
user.setName("mx");
user.setAge(18);
user.setAddress("wenzhou");
return user;
}
}
index.htm
CORS
当前网页来自localhost:7070/index.html
页面加载时自动发送GET请求: http://localhost:8080/avatar.png
![](http://localhost:8080/avatar.png)
点击发送GET请求: http://localhost:8080/getUser/1
JQuery你们可以引用外站的:
刚才提到过,协议、端口、域名都能造成跨域,这里我们演示最简单的跨域:端口不同导致跨域,我会把同一个项目启动两次。
第一次通过IDEA启动7070端口:
第二次通过java -jar指定8080端口启动应用(或者可以再配置一个IDEA的启动按钮,更改yml端口后启动即可):
# install
mvn clean install -Dmaven.test.skip=true
# 指定8080端口启动项目
java -jar /Users/kevin/IdeaProjects/springboot-demo/target/springboot-demo-0.0.1-SNAPSHOT.jar --server.port=8080
请求端口为7070的应用,得到index.html页面,随后点击按钮,向端口为8080的应用发起请求:
index.html来自端口为7070的应用,它总共向端口为8080的应用发送两次请求:
但botton的GET请求真的失败了吗?其实AJAX已经拿到了数据(状态码200),只不过浏览器拒绝该数据。
回顾一下开头的那张示意图:
本案例中,页面index.html来自localhost:7070,而和AJAX的GET都指向localhost:8080,都跨域了:
但为什么只有AJAX被拒绝了?
因为浏览器允许跨域。
是的,浏览器遵守同源策略,但是有若干个标签是允许跨域的,比如:
我之前说Jquery脚本可以直接引用外站的,就是这个道理,因为