什么是跨域?
跨域是浏览器行为,是浏览器的一种安全策略;由于浏览器 同源策略 导致浏览器访问服务器时被拦截
同源策略(Sameoriginpolicy):
是一种约定,它是浏览器最核心也最基本的安全功能,如果缺少了同源策略,则浏览器的正常功能可能都会受到影响。可以说Web是构建在同源策略基础之上的,浏览器只是针对同源策略的一种实现。同源策略会阻止一个域的javascript脚本和另外一个域的内容进行交互。所谓同源(即指在同一个域)就是两个页面具有相同的协议 (protocol),主机 (host)和端口号(port)
这里还需要提一下http的url组成部分:
HTTP URL 由以下几个部分组成:
1,协议(Scheme):定义因特网服务的类型。最常见的是 http 和 https,分别用于普通和加密的 HTTP 服务。
2,主机(Host):服务器的域名系统 (DNS) 名称或 IP 地址。
3,端口(Port):可选,用于标识服务器上的特定网络程序;如果未指定,默认端口分别是 80 和 443 对应于 http 和 https。
4,路径(Path):服务器上的路径,标识特定资源或服务。
5,查询(Query):可选,发送到服务器的数据;以 ? 开始,可以包含键值对,用 & 分隔。
6,片段(Fragment):可选,用于指导浏览器跳转到页面的特定部分。
以下面地址为例
javascript
http://www.example.com:80/path/to/resource?key=value#section
协议:http
主机:www.example.com
端口:80(默认)
路径:/path/to/resource
查询:key=value
片段:section
如下表所示,协议,主机与端口导致的跨域例子
地址一 | 地址二 | 是否跨域 | 跨域原因 |
---|---|---|---|
https://www.baidu.com | http://www.baidu.com | 是 | https/http协议不同 |
https://www.baidu.com | https://www.example.com | 是 | 主机不同 |
https://www.baidu.com | https://www.baidu.com:80 | 是 | 端口不同,https默认端口是443,http默认端口是80 |
https://www.baidu.com | https://www.baidu.com:443 | 否 | 端口一致 |
需要注意的是:跨域不一定都会有跨域问题。
因为跨域问题是浏览器对于ajax请求的一种安全限制:一个页面发起的ajax请求,只能是与当前页域名相同的路径,这能有效的阻止跨站攻击。
因此:跨域问题 是针对ajax的一种限制。
但是这却给我们的开发带来了不便,而且在实际生产环境中,肯定会有很多台服务器之间交互,地址和端口都可能不同,怎么办?
解决跨域问题的方法
1,jsonp解决跨域
JSONP(JSON with Padding) 是 json 的一种"使用模式",可以让网页从别的域名(网站)那获取资料,即跨域读取数据。
原理:
html中的src(获取图片),的href(获取css),script的src(获取javascript)这三个标签都不符合同源策略,它们可以跨域获取数据。JSONP就是通过动态创建
实现封装jsonp的代码:
javascript
function jsonp({ url, params, callback }) {
return new Promise((resolve, reject) => {
let script = document.createElement("script");
// 定义一个全局回调函数,若请求返回,则执行里边内容
window[callback] = function(data) {
resolve(data);
document.body.removeChild(script);
}
let arrs = [];
params = { ...params, callback };
for (let key in params) {
arrs.push(params[key]);
}
script.src = `${url}?${arrs.join("&")}`;
document.body.appendChild(script);
});
}
jsonp({
url: "http://localhost:3000/say",
params: { wd: "Iloveyou" },
callback: "show"
}).then(data => {
console.log(data);
});
我们也可以直接在html中使用script标签
html前端代码
javascript
<script src="http://localhost:3000/getData?callback=func"><script>
<script>
function func(res) {
alert(res.message + res.name + "你永远" + res.age + "岁");
}
</script>
服务端代码:
javascript
router.get('/getData', (req, res) => {
let data = {
message: 'success!',
name: '胡不说',
age: 18
}
data = JSON.stringify(data)
res.end('func(' + data + ')');
});
jsonp的优缺点
优点 | 缺点 |
---|---|
1,不受同源策略限制,实现跨域; 2,使用简单,不需要配置代理 3,兼容性好,低版本ie也能兼容,基本不存在兼容性问题 | 1,只支持get请求 2,jsonp在调用失败的时候不会返回各种HTTP状态码 3,跳过了同源策略,安全性没办法保证 |
2,cors头设置
这个一般情况跟前端没啥关系,需要后端去配置放开跨域白名单。
前端发现跨域了
具体沟通情况可参照如下:
javascript
前端:后端大大,我这边调您接口好像跨域了,我需要怎么做才能解决这个问题呢?
后端:我现在好忙,你看看能不能自己解决一下
前端:是这样的,我这边配置代理挺麻烦的,不过我先尝试一下(配置不配置再说,复不复杂再说,晚点再沟通)
过了一会。。。
前端:后端大大,我没解决
后端:哦,那我来吧
node设置白名单解决跨域
javascript
const http = require('http')
const url = require('url')
// 创建server
const server = http.createServer()
// 定义跨域访问白名单
const authOrigin = ['http://127.0.0.1:5500']
// 监听http请求
server.on('request', (req, res) => {
const user = { // 模拟返回数据
id: 1,
name: 'zhangsan',
age: 12
}
const origin = req.headers.origin
if(authOrigin.includes(origin)) {
// 添加响应头,实现cors
res.setHeader('Access-Control-Allow-Origin', '*') // 允许所有的地址跨域访问
//res.setHeader('Access-Control-Allow-Origin', origin) // 只有白名单中的地址才可以跨域访问
}
res.end(JSON.stringify(user))
})
// 设置监听端口
server.listen(8081, function() {
console.log('server is running on 8081 port!')
})
java设置白名单解决跨域
java
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Bean
public WebMvcConfigurer corsConfigurer() {
return new WebMvcConfigurer() {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**") // 允许跨域的路径
.allowedOrigins("*") // 允许跨域请求的域名
.allowedMethods("GET", "POST", "PUT", "DELETE") // 允许的请求方法
.allowedHeaders("*") // 允许的请求头
.allowCredentials(true); // 是否允许证书(cookies)
}
};
}
}
3,代理服务器(Proxy Server)
同源策略是浏览器行为,那我们通过代理服务器访问就不受同源策略的限制了啊,不管是proxy还是nginx代理都是通过这个原理解决的跨域问题
我们以vue3 vite构建项目为例:
1,在项目根目录中的 vite.config.js 文件中,配置代理服务器:
javascript
import { defineConfig } from 'vite';
export default defineConfig({
server: {
proxy: {
'/api': {
target: 'http://target-domain.com', // 目标服务器地址
changeOrigin: true, // 是否改变源地址
rewrite: (path) => path.replace(/^\/api/, '') // 重写路径
}
}
}
});
2,在发送请求时,使用配置好的代理路径(例如 /api):
javascript
import axios from 'axios';
axios.get('/api/some-endpoint').then(response => {
console.log(response.data);
});
这样,所有发往 /api 的请求都会被代理到 http://target-domain.com,从而绕过同源策略,实现跨域通信。
4,Websocket
Websocket 是 HTML5 的一个持久化的协议,它实现了浏览器与服务器的全双工通信,同时也是跨域的一种解决方案。
然后,不会有人用这个方法解决跨域的,Websocket实时通讯有它自己的使用场景,我们需要知道的就是它没有跨域问题就足够了
5,利用iframe标签
这个与jsonp类似,html的iframe标签不受同源策略影响,可以通过它解决跨域;了解有这个方法就行,除非特殊场景,一般也不会用它
总结一下
1,jsonp
2,cors头设置
3,搭建代理服务器
4,Websocket
5,利用iframe标签
整理知识点,基础不牢,地动山摇。