什么是跨域?
跨域是浏览器行为,是浏览器的一种安全策略;由于浏览器 同源策略 导致浏览器访问服务器时被拦截
同源策略(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):可选,用于指导浏览器跳转到页面的特定部分。
以下面地址为例
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),
实现封装jsonp的代码:
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前端代码
<script src="http://localhost:3000/getData?callback=func"><script><script>function func(res) {alert(res.message + res.name + "你永远" + res.age + "岁");}</script>
服务端代码:
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头设置
这个一般情况跟前端没啥关系,需要后端去配置放开跨域白名单。
前端发现跨域了
具体沟通情况可参照如下:
前端:后端大大,我这边调您接口好像跨域了,我需要怎么做才能解决这个问题呢?后端:我现在好忙,你看看能不能自己解决一下前端:是这样的,我这边配置代理挺麻烦的,不过我先尝试一下(配置不配置再说,复不复杂再说,晚点再沟通)过了一会。。。前端:后端大大,我没解决后端:哦,那我来吧
node设置白名单解决跨域
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.originif(authOrigin.includes(origin)) {// 添加响应头,实现corsres.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设置白名单解决跨域
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 {@Beanpublic WebMvcConfigurer corsConfigurer() {return new WebMvcConfigurer() {@Overridepublic 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 文件中,配置代理服务器:
import { defineConfig } from 'vite';export default defineConfig({server: {proxy: {'/api': {target: 'http://target-domain.com', // 目标服务器地址changeOrigin: true, // 是否改变源地址rewrite: (path) => path.replace(/^\/api/, '') // 重写路径}}}
});
2,在发送请求时,使用配置好的代理路径(例如 /api):
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标签
整理知识点,基础不牢,地动山摇。