同源政策与跨域
@
相关文章收集
【腾讯文档】面试官:请说下有哪些跨域解决方案
1. 同源政策
① 什么是同源#
如果两个页面拥有相同的协议、域名和端口,那么这两个页面就属于同一个源,其中只要有一个不相同,就是不同源。
比如 相对于 http://www.test.com/index.html 页面的同源检测:
URL | 是否同源 | 原因 |
---|---|---|
http://www.test.com/other.html | 是 | 同源(协议、域名、端口相同) |
https://www.test.com/about.html | 否 | 协议不同(http 与 https) |
http://blog.test.com/movie.html | 否 | 域名不同(www.test.com 与 blog.test.com) |
http://www.test.com:7001/home.html | 否 | 端口不同(默认的 80 端口与 7001 端口) |
http://www.test.com:80/main.html | 是 | 同源(协议、域名、端口相同) |
② 什么是同源策略#
同源策略(英文全称 Same origin policy)是 浏览器 提供的一个 安全功能。
MDN 官方给定的概念:同源策略限制了从同一个源加载的文档或脚本如何与来自另一个源的资源进行交互。这是一个用于隔离潜在恶意文件的重要安全机制。
浏览器的同源安全策略默认会阻止网页“跨域”获取资源,例如:
- 无法读取非同源网页的 Cookie、LocalStorage 和 IndexedDB
- 无法接触非同源网页的 DOM
- 无法向非同源地址发送 Ajax 请求
作用
同源政策是为了保证用户信息的安全,防止恶意的网站窃取数据。最初的同源政策是指 A 网站在客户端设置的Cookie,B网站是不能访问的。随着互联网的发展,同源政策也越来越严格,在不同源的情况下,其中有一项规定就是无法向非同源地址发送Ajax 请求,如果请求,浏览器就会报错。
2. 跨域
① 什么是跨域#
同源指的是两个 URL 的协议、域名、端口一致,反之,则是跨域。
出现跨域的根本原因:浏览器的同源策略不允许非同源的 URL 之间进行资源的交互。
网页:http://www.test.com/index.html
接口:http://www.api.com/userlist
② 浏览器对跨域请求的拦截#
注意:浏览器允许发起跨域请求,但是,跨域请求回来的数据,会被浏览器拦截,无法被页面获取到!
③ 如何实现跨域数据请求#
-
JSONP
:出现的早,兼容性好(兼容低版本IE)。是前端开发者为了解决跨域问题,被迫想出来的一种 临时解决方案。 -
CORS
:出现的较晚,它是 W3C 标准,属于跨域 Ajax 请求的根本解决方案。支持 GET 和 POST 请求。缺点是不兼容某些低版本的浏览器。
3. 使用 JSONP 实现跨域请求
① 什么是JSONP#
jsonp (json with padding) 是 JSON 的一种“使用模式” ,可用于解决主流浏览器的跨域数据访问的问题
② JSONP的实现原理#
由于浏览器同源策略的限制,网页中无法 请求非同源的接口数据。但是<script>
标签不受浏览器同源策略的影响,可以通过 src 属性,请求非同源的 js 脚本。
<script>
标签说明:
<script>
标签默认发起get请求
标签的src属性的请求地址可以是任意属性的,不一定非得要以.js
结尾; 但是注意❕, 不管请求地址的形式的怎样的,但它都得返回JavaScript代码脚本
因此,JSONP 的实现原理,就是通过 <script>
标签的 src 属性,请求跨域的数据接口,并通过函数调用的形式,接收跨域接口响应回来的数据。
它不属于 Ajax 请求,jsonp方案绕过了同源限制,并通过<script>
模拟了 Ajax 请求。
由于 JSONP 是通过<script>
标签的 src 属性,来实现跨域数据获取的,所以,JSONP 只支持 GET 数据请求, 不支持 POST、PUT、DELETE 等请求。
注意:JSONP 和 Ajax 之间没有任何关系,不能把 JSONP 请求数据的方式叫做 Ajax,因为 JSONP 没有用到XMLHttpRequest
这个对象。
③ JSONP 实现基本步骤#
-
客户端将跨域请求地址写在
script
标签的src
属性中<script src="https://www.example.com"></script> <!-- 比如: <script src="https://cdn.bootcss.com/jquery/3.3.1/jquery.min.js"></script> -->
-
服务器端响应数据必须是一个函数的调用,真正要发送给客户端的数据需要作为函数调用的参数。
const data = 'fn({name: "张三", age: "20"})'; res.send(data);
-
在客户端全局作用域下定义函数
fn
,注意,须在调用之前定义好函数; 并在在 fn 函数内部对服务器端返回的数据进行处理function fn (data) { console.log(data); }
example
<!-- 定义一个 success 回调函数 -->
<script>
function success(data) {
console.log('获取到了data数据:')
console.log(data)
}
</script>
<!-- 通过 <script> 标签,跨域请求接口数据: -->
<script src="http://www.example.com:3006/api/jsonp?callback=success&name=zs&age=20"></script>
④ 自定义 JSONP功能 封装函数#
//功能: 发送非同源请求,接收后台响应数据
function jsonp (options) {
// 动态创建script标签
var script = document.createElement('script');
// 拼接字符串的变量
var params = '';
//请求参数拼接
for (var attr in options.data) {
params += '&' + attr + '=' + options.data[attr];
}
// 生成随机函数名, 防止函数覆盖; myJsonp0124741
var fnName = 'myJsonp' + Math.random().toString().replace('.', '');
//把随机函数名添加到全局作用域当中
window[fnName] = options.success;
// 为script标签添加src属性
script.src = options.url + '?callback=' + fnName + params;
// 将script标签追加到页面中, 发送请求
document.body.appendChild(script);
// 为script标签添加onload事件, 当script标签加载完毕并发送完请求后在dom中删除
script.onload = function () {
document.body.removeChild(script);
}
}
//客户端函数调用, 发送非同源请求
jsonp({
//script标签的请求地址,String
url: 'http://localhost:3001/login';
//请求参数,Array; {name: "lisi", age: 12}
data: {
name: "lisi",
age: 12
}
//success: 响应执行函数, data: 接收响应返回数据; function(data){}
success: function(data) {
console.log(data)// Object {name: 'lisi', age: 20}
}
})
//服务端响应请求
const express = require('express');
const app = express();
app.get('login', (req, res) => {
//jsonp(parms) :响应 参数 callback 函数的调用的字符串, parms: 传递给callback函数的参数
res.jsonp({name: "lisi", age: 12})
})
⑤ jQuery中的JSONP#
jQuery 中的 JSONP,也是通过 <script>
标签的 src 属性实现跨域数据访问的,只不过,jQuery 采用的是动态创建和移除 <script>
标签的方式,来发起 JSONP 数据请求, 逻辑和上诉自定义jsonp
函数类似
- 在发起 JSONP 请求的时候,动态向
<header>
中 append 一个<script>
标签; - 在 JSONP 请求成功以后,动态从
<header>
中移除刚才 append 进去的<script>
标签;
jQuery 提供的 $.ajax()
函数通过指定参数:
dataType: jsonp
: 还能够发起 JSONP 数据请求jsonp
: 自定义参数名称jsonpCallback
: 自定义回调函数名称
例如:
$.ajax({
url: 'http://www.example.com:3006/api/jsonp?name=zs&age=20',
dataType: 'jsonp',
// 发送到服务端的参数名称,默认值为 callback
jsonp: 'callback',
// 自定义的回调函数名称,默认值为 jQueryxxx 格式
jsonpCallback: 'abc',
success: function(res) {
console.log(res)
}
})
默认情况下,使用 jQuery 发起 JSONP 请求,会自动携带一个callback=jQueryxxx
的参数,jQueryxxx 是随机生成的一个回调函数名称。
4. CORS 跨域资源共享
① 什么是 CORS#
CORS (Cross-Origin Resource Sharing,跨域资源共享)由一系列 HTTP 响应头组成,这些 HTTP 响应头决定浏览器是否阻止前端 JS 代码跨域获取资源。
即跨域资源共享,它允许浏览器向跨域服务器发送 Ajax 请求,克服了 Ajax 只能同源使用的限制。
CORS 方案与 Jsonp方案有所不同 ,通过对服务器进行配置来决定是否同意跨域请求,该方案能使得 Ajax请求直接跨域访问
② CORS 响应头部 - Access-Control-Allow-Origin#
过程描述:
- 浏览器判断当前请求是否非同源,如果是,会在请求头中添加
origin
字段,字段值为当前发送请求的域信息(当前网站的页面地址,包含协议、域名、端口号),该过程由浏览器自动完成origin: http://localhost:3000
- 服务器根据当前请求的 origin 字段来响应这次请求,服务端通过在响应头中是否添加
Access-Control-Allow-Origin
字段来告诉浏览器是否同意这次请求,字段值是 被允许请求的客户端信息,值通常 被允许访问该资源的外域URL 或者*
号,该过程需要开发者手动完成//指定允许的客户端 Access-Control-Allow-Origin: 'http://localhost:3000' //允许所有客户端访问该服务器端 Access-Control-Allow-Origin: '*'
example:
//利用express 提供的中间件拦截请求, 并配置服务端接收客户端跨域请求
app.use((req, res, next) => {
//1.允许哪些客户端访问我, "*"代表所有客户端
res.setHeader('Access-Control-Allow-Origin', '*');
})
CORS 的注意事项
- CORS 主要在 服务器端 进行配置。客户端浏览器 无须做任何额外的配置,即可请求开启了 CORS 的接口。
- CORS 在浏览器中有 兼容性。只有支持 XMLHttpRequest Level2 的浏览器,才能正常访问开启了 CORS 的服务端接口(例如:IE10+、Chrome4+、FireFox3.5+)。
③ CORS 响应头部 - Access-Control-Allow-Headers#
默认情况下,CORS 仅支持客户端向服务器发送的请求头中字段如下的 9 个:
- Accept
- Accept-Language
- Content-Language
- DPR
- Downlink
- Save-Data
- Viewport-Width
- Width
- Content-Type (值仅限于 text/plain、multipart/form-data、application/x-www-form-urlencoded 三者之一)
如果客户端向服务器 发送了额外的请求头信息,则需要在服务器端,通过 Access-Control-Allow-Headers
对额外的请求头进行声明,否则这次请求会失败!
// 允许客户端额外向服务端发送 Content-Type 请求头和 X-Custom-Header 请求头
// 注意: 多个请求头之间使用英文的逗号进行分割
res.setHeader('Access-Control-Allow-Headers', 'Content-Type, X-Custom-Header')
④ CORS 响应头部 - Access-Control-Allow-Methods#
默认情况下,CORS 仅支持客户端发起 GET、POST、HEAD 请求。
如果客户端希望通过PUT、DELETE
等方式请求服务器的资源,则需要在服务器端,通过 Access-Control-Alow-Methods
来指明实际请求所允许使用的 HTTP 方法。
示例如下:
// 只允许 POST、GET、DELETE、HEAD 请求方法
res.setHeader('Access-Control-Allow-Methods', 'POST, GET, DELETE, HEAD')
// 允许所有的 HTTP 请求方法
res.setHeader('Access-Control-Allow-Methods', '*')
⑤ CORS请求的分类#
客户端在请求 CORS 接口时,根据 请求方式 和 请求头 的不同,可以将 CORS 的请求分为两大类,分别是:
- 简单请求
- 预检请求
简单请求#
同时满足以下两大条件的请求,就属于简单请求:
- 请求方式:GET、POST、HEAD 三者之一
- HTTP 头部信息 不超过以下几种字段:无自定义头部字段、Accept、Accept-Language、Content-Language、DPR、Downlink、Save-Data、Viewport-Width、Width 、Content-Type(只有三个值application/x-www-form-urlencoded、multipart/form-data、text/plain)
预检请求#
只要符合以下任何一个条件的请求,都需要进行预检请求:
- 请求方式为 GET、POST、HEAD 之外的请求 Method 类型
- 请求头中包含自定义头部字段
- 向服务器发送了
application/json
格式的数据
在浏览器与服务器正式通信之前,浏览器会先发送 OPTION 请求进行预检,以获知服务器是否允许该实际请求,所以这一次的 OPTION 请求称为“预检请求”。服务器成功响应预检请求后,才会发送真正的请求,并且携带真实数据。
两种请求的区别#
简单请求的特点:客户端与服务器之间 只会发生一次请求。
预检请求的特点:客户端与服务器之间会发生两次请求,OPTION 预检请求成功之后,才会发起真正的请求。
5. 服务器端之间的请求不存在跨域问题
同源政策是浏览器给予的限制,服务器端是不存在同源政策限制。
所以,可以通过服务器端发送请求直接访问非同源网站的数据,再把所获取的非同源数据响应给客户端。
6. 跨域请求中的 cookie 问题
withCredentials 属性#
在使用 Ajax 技术发送跨域请求时,默认情况下不会在请求中携带 cookie
信息。
需要同时在客户端和服务端做如下设置才能解决ajax跨域请求cookie
问题
-
在客户端中,在通过
send()
发送ajax请求前需要设置ajax对象的withCredentials
属性// 指定在涉及到跨域请求时,是否携带 cookie 信息,默认值为 false new XMLHttpRequest().withCredentials = true;
-
在服务端中,通过
res.hender
设置Access-Control-Allow-Credentials
字段app.use('/login', (req, res, next) => { //允许客户端发送请求时携带 cookie res.header("Access-Control-Allow-Credentials", "true"); })
作者:Hong•Guo
出处:https://www.cnblogs.com/ghnb1/p/15851741.html
版权:本作品采用「署名-非商业性使用-相同方式共享 4.0 国际」许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· 【自荐】一款简洁、开源的在线白板工具 Drawnix