一、什么是跨域
在浏览器中,存在一种安全机制叫做 同源策略(Same-Origin Policy) 。
同源策略规定:
一个网页的脚本只能访问与该网页 同源 的资源。
所谓 同源(Origin) 指的是:
协议 + 域名 + 端口
三者必须 完全一致 才算同源。
例如:
| 页面地址 | 请求地址 | 是否同源 |
|---|---|---|
| a.com | a.com/api | 是 |
| a.com | a.com/api | 否(协议不同) |
| a.com | b.com/api | 否(域名不同) |
| a.com:3000 | a.com:4000 | 否(端口不同) |
只要 协议、域名、端口有一个不同,就属于 不同源请求(跨源请求) 。
当浏览器阻止这种请求时,就产生了我们常说的 跨域问题。
二、为什么浏览器要限制跨域
浏览器限制跨域主要是为了 安全原因,防止恶意网站窃取用户信息。
例如:
用户登录了
bank.com
浏览器中已经保存了登录 Cookie。
如果没有同源策略,用户访问恶意网站:
hack.com
hack.com 就可以执行:
fetch("https://bank.com/api/userinfo")
从而获取用户的:
- 银行账户
- 余额
- 交易记录
这会造成严重的信息泄露问题。
因此浏览器规定:
一个网站的 JavaScript 不能随意读取另一个网站的数据。
这就是同源策略存在的原因。
三、跨域什么时候会出现
跨域通常出现在 前端请求后端接口时。
例如在开发环境中:
前端运行在:
http://localhost:8081
后端接口运行在:
http://localhost:8080
两者比较:
| 项目 | 前端 | 后端 |
|---|---|---|
| 协议 | http | http |
| 域名 | localhost | localhost |
| 端口 | 8081 | 8080 |
由于 端口不同,所以属于 不同源请求。
当使用 AJAX 请求接口时:
axios.get("http://localhost:8080/api/user")
浏览器会认为这是 跨域请求。
如果服务器没有返回 CORS 允许信息,浏览器就会报跨域错误。
四、跨域请求不一定都会被拦截
需要注意的是:
不同源请求 ≠ 一定报跨域
浏览器实际上允许 某些资源跨域加载。
例如:
<img>
<script>
<link>
<video>
<audio>
例如:
<img src="http://other.com/a.png">
这是跨域请求,但浏览器允许加载。
原因是:
这些资源只能被加载,但 JavaScript 无法读取返回的数据。
因此安全风险较小。
五、为什么图片可以跨域加载,而 AJAX 不可以
浏览器对跨域请求做了一个重要区分:
1 资源加载(允许)
例如:
<img>
<script>
<link>
特点:
- 可以跨域加载
- JavaScript 无法读取返回的数据
例如图片:
<img src="http://b.com/a.png">
JS 无法获取图片的真实内容数据。
因此浏览器认为风险较小。
2 数据读取(限制)
AJAX 属于 数据读取行为:
fetch("http://b.com/api/user")
特点:
- JS 可以直接获取返回数据
- 数据可能包含敏感信息
例如:
用户信息
银行余额
订单数据
因此浏览器默认禁止这种跨域访问。
只有当服务器明确允许时,浏览器才会放行。
服务器需要返回:
Access-Control-Allow-Origin
例如:
Access-Control-Allow-Origin: http://a.com
这就是 CORS(跨域资源共享)机制。
六、JSONP 技术
在 CORS 出现之前,前端常用 JSONP 技术解决跨域问题。
JSONP 的核心思想是:
利用
<script>标签可以跨域加载资源的特点来获取数据。
因为 <script> 标签不受同源策略限制。
1 JSONP 的实现原理
JSONP 的流程如下:
第一步:前端定义回调函数
function callback(data){
console.log(data)
}
第二步:动态创建 script 标签
const script = document.createElement("script")
script.src = "http://api.com/user?callback=callback"
document.body.appendChild(script)
浏览器会请求:
http://api.com/user?callback=callback
第三步:服务器返回 JS 代码
服务器返回的不是 JSON,而是:
callback({
"name":"Tom",
"age":20
})
第四步:浏览器执行代码
浏览器加载 script 后,会执行:
callback({
"name":"Tom",
"age":20
})
从而调用前端函数,得到数据。
七、JSONP 的优缺点
优点
- 可以解决跨域问题
- 实现方式简单
缺点
1 只支持 GET 请求
因为 <script> 只能使用 GET。
不能发送:
POST
PUT
DELETE
2 存在安全问题
服务器返回的是 JavaScript 代码。
如果服务器被攻击,可能返回恶意代码:
alert("恶意代码")
浏览器会直接执行。
3 错误处理困难
AJAX 可以处理:
status
error
timeout
JSONP 很难实现这些功能。
八、现代解决跨域的方式
目前主流解决跨域的方法是 CORS。
服务器只需要返回:
Access-Control-Allow-Origin
例如:
Access-Control-Allow-Origin: *
或者:
Access-Control-Allow-Origin: http://localhost:8081
这样浏览器就允许前端访问接口。
九、总结
本次学习主要理解了 跨域产生的原因以及解决方式。
核心知识点如下:
- 浏览器存在 同源策略。
- 同源由 协议 + 域名 + 端口 共同决定。
- 只要其中一个不同,就属于 跨源请求。
- 浏览器允许 跨域加载资源,但不允许 跨域读取数据。
- AJAX 属于数据读取,因此会被限制。
- JSONP 通过
<script>标签跨域加载实现数据获取,但只支持 GET。 - 现代开发通常使用 CORS 解决跨域问题。