跨域:当页面运行中请求的url地址的源和当前页面url地址的源不相同时,就发生了跨域(也叫跨源)
浏览器的同源策略会对跨域请求有限制,其中对ajax的限制最为严格:不允许ajax访问跨域资源
源 = 协议 + 主机 + 端口
跨域的解决方案
代理(适用开发环境跨域)
通过配置开发服务器,来进行拦截转发,例如vue项目,可以在vue.config.js中配置
// 配置代理解决、vue.config.js
devServer: {
proxy: {
"/api": {
target: "http://your-backend-api.com", // 目标接口域名
changeOrigin: true, // 是否启用代理
pathRewrite: { "^/api": "" } // 重写路径,去掉/api
}
}
}
/api 表示开发服务器会拦截路径以/api开头的请求,并转发请求到target的地址,开发服务器拿到响应后,交给浏览器,在浏览器看来这次请求并没有跨域,所以允许ajax访问资源
可以看出,浏览器同源策略只检查协议,主机和端口号,而并不限制端口号后面的路径path,所以代理正是利用path中的信息来瞒过浏览器的限制
Nginx反向代理(适用生产环境跨域)
Nginx反向代理也是利用Nginx服务器转发请求,从而跳过浏览器的限制
server {
listen 80;
server_name frontend.com;
location /api/ {
proxy_pass http://your-backend-api.com;
add_header Access-Control-Allow-Origin *;
}
}
CORS
CORS(跨域资源共享)是后端服务器要进行一些处理。服务器在响应头里增加一些属性,就可以让浏览器允许ajax访问相应资源。CORS对于不同的跨域请求有三种处理模式
1.简单请求
| 请求方法 | 请求头 |
|---|---|
| POST/GET/HEAD | 没有自定义的请求头. 并且如果请求头中有Content-Type属性,只能是“text/plain”,"multipart/form-data","application/x-www-form-urlencoded"中的一种 |
对于简单请求,后端响应需在响应头中加入属性:
Access-Control-Allow-Origin:请求的源 (如果是*号,则代表允许所有源)
2.需要预检的请求
如果不满足上面的简单请求判断条件,那就是需要预检的请求 对于需要预检的请求,浏览器在发送真正的请求前,先发送一个预检请求,用于向服务器确认是否允许实际请求
OPTIONS /api/data HTTP/1.1 //预检请求的请求方式为OPTIONS
Host: example.com //请求的主机
Origin: https://your-site.com //发起请求的源
Access-Control-Request-Method: POST //实际请求将使用的 HTTP 方法
Access-Control-Request-Headers: Content-Type,a,b //实际请求将携带的自定义头字段
服务器如果允许实际请求,则需在响应头中添加以下属性
Access-Control-Allow-Origin: https://your-site.com //实际请求的源
Access-Control-Allow-Methods: POST, GET, OPTIONS //需包含实际请求的请求方法
Access-Control-Allow-Headers: Content-Type, a, b //需包含实际请求的头字段
Access-Control-Max-Age: 86400 // 可选:缓存预检结果的秒数,表示在这个时间内不必再发起预检请求可以直接请求
随后浏览器就可以发真实请求,与简单请求相同,需要响应头中设置
Access-Control-Allow-Origin:请求的源
3.携带身份凭证的请求
前端配置: 默认情况下,ajax的跨域不会附带cookie,需要前端增加配置
fetch: credentials: 'include'
fetch('https://api.example.com/data', {
method: 'GET',
credentials: 'include', // 必须设置为 include 以携带 Cookie
headers: {
'Content-Type': 'application/json',
},
})
.then(response => response.json())
.catch(error => console.error('Error:', error));
xhr: xhr.withCredentials = true
const xhr = new XMLHttpRequest();
xhr.open('GET', 'https://api.example.com/data', true);
xhr.withCredentials = true; // 关键配置,携带 Cookie
xhr.onreadystatechange = function() {
if (xhr.readyState === 4 && xhr.status === 200) {
console.log(xhr.responseText);
}
};
xhr.send();
服务器配置
Access-Control-Allow-Origin: https://your-frontend.com // 必须明确指定域名,不能为 *
Access-Control-Allow-Credentials: true //明确允许凭证cookie
Access-Control-Allow-Methods: GET, POST, PUT, OPTIONS // 允许的方法
Access-Control-Allow-Headers: Content-Type, Authorization // 允许的请求头
简单请求和需要预检的请求都有可能携带凭证,无论是哪一种,响应头都需要显式声明允许凭证
Access-Control-Allow-Credentials: true //明确允许凭证cookie
Access-Control-Allow-Origin:请求的源 (不能是*)
JSONP(兼容老系统)
JSONP是在没有CORS之前的一种解决方案,通过生成一个script标签,并把src设置为要请求的地址
<script src="http://crossdomin.com/api/user"></script>
并且需要后端返回响应为一个函数,并将前端需要的数据作为函数的参数传入,这样浏览器请求script资源后,会执行js文件,也就会执行这个函数,前端在这个函数里就可以拿到需要的数据。
缺点:只支持GET请求