什么是跨域
跨域,即跨域资源共享(CORS,Cross-Origin Resource Sharing),是一个W3C标准,它定义了一种浏览器和服务器交互的方式来确定是否允许跨源请求。简单来说,跨域就是浏览器的同源策略导致来自不同源的脚本在没有明确授权的情况下,不能读写对方的资源。
跨域是指浏览器出于安全考虑,限制网页脚本访问不同源(协议、域名、端口)的资源。两个URL的协议、域名或端口任意一个不相同时,就属于不同源,浏览器会阻止脚本请求从一个源加载的文档与另一个源的资源进行交互。
跨域产生的原因
跨域问题产生的根本原因是浏览器的同源策略(Same-Origin Policy)。同源策略是浏览器实现的一种安全协议,它限制了一个源的文档或脚本如何与另一个源的资源进行交互。如果没有同源策略,恶意网页可能会读取另一个网页的敏感信息,如用户输入的密码、银行账号等,从而进行非法操作。
跨域的常见解决方案
(一) CORS(跨域资源共享)
CORS是W3C标准,它定义了一种浏览器和服务器交互的方式来确定是否允许跨源请求。通过服务器响应头,告知浏览器允许的跨域请求来源与方法。
服务端配置示例(Node.js):
1
2
3
4
5
6
7
|
app.use((req, res, next) => {
res.setHeader('Access-Control-Allow-Origin', 'https://www.my-domain.com'); // 指定允许的域名
res.setHeader('Access-Control-Allow-Methods', 'GET, POST, OPTIONS'); // 允许的HTTP方法
res.setHeader('Access-Control-Allow-Headers', 'Content-Type, Authorization'); // 允许的请求头
res.setHeader('Access-Control-Allow-Credentials', 'true'); // 允许携带Cookie
next();
});
|
服务端配置示例(Spring Boot):
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
|
// 方法1:直接怼注解(适合单个接口)
@CrossOrigin(origins = "http://localhost:8080")
@GetMapping("/user")
public User getUser() { ... }
// 方法2:全局配置(一劳永逸)
@Configuration
public class CorsConfig implements WebMvcConfigurer {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.allowedOrigins("http://localhost:8080")
.allowedMethods("*")
.allowedHeaders("*")
.allowCredentials(true)
.maxAge(3600);
}
}
// 方法3
@Configuration
public class CorsConfig
{
/**
* 跨域配置
*/
@Bean
public CorsFilter corsFilter()
{
CorsConfiguration config = new CorsConfiguration();
// 设置访问源地址
config.addAllowedOriginPattern("*");
// 设置访问源请求头
config.addAllowedHeader("*");
// 设置访问源请求方法
config.addAllowedMethod("*");
// 有效期 1800秒
config.setMaxAge(1800L);
// 添加映射路径,拦截一切请求
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", config);
// 返回新的CorsFilter
return new CorsFilter(source);
}
}
|
前端配置示例:
1
2
3
4
|
fetch('https://api.other-domain.com/data', {
mode: 'cors', // 添加此行,表示这是一个跨源请求
credentials: 'include' // 添加此行,表示请求中包含凭据
});
|
为什么本地不会复现跨域问题?
Vue 项目中vue.config.js(Vue CLI)或vite.config.js(Vite)配置的代理,仅作用于开发环境(dev) ,生产构建(build)后会失效。
- 开发代理是 Node.js 开发服务器提供的临时转发能力,用于解决本地跨域问题;
- 生产构建后生成的是纯静态文件(HTML/CSS/JS),无内置服务器,自然无法提供代理服务。
(二) JSONP
JSONP(JSON with Padding)是一种古老的跨域解决方案,主要用于GET请求。JSONP的实现方式是通过script标签来加载跨域的JavaScript文件,该文件返回的内容是一个函数调用,函数参数即为所请求的数据。
前端代码示例:
1
2
3
4
|
<script src="https://api.example.com/data?callback=myCallback"></script>
function myCallback(data) {
console.log(data);
}
|
后端代码示例(Node.js):
1
2
3
4
5
|
app.get('/data', (req, res) => {
const data = { key: 'value' };
const callback = req.query.callback;
res.send(`${callback}(${JSON.stringify(data)})`);
});
|
(三) 代理
通过设置代理,前端请求会先发送到一个同域的代理服务器,由代理服务器转发请求到目标服务器。这种方式通常用于开发环境中解决跨域问题。
代理配置示例(Node.js):
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
const express = require('express');
const proxy = require('http-proxy-middleware');
const app = express();
app.use('/api', proxy({
target: 'https://api.example.com', // 目标服务器地址
changeOrigin: true, // 更改请求头中的host
pathRewrite: {
'^/api': '' // 重写路径
}
}));
app.listen(3000, () => {
console.log('服务器运行在 http://localhost:3000');
});
|
(四) Nginx反向代理
Nginx反向代理也是一种常见的跨域解决方案。通过Nginx配置,将前端请求转发到后端服务器,同时设置响应头以解决跨域问题。
1
2
3
4
5
6
7
8
9
10
11
|
server {
listen 80;
server_name localhost;
location /api {
proxy_pass http://api.xxx.com:8000;
add_header 'Access-Control-Allow-Origin' 'http://localhost:8080';
add_header 'Access-Control-Allow-Methods' 'GET,POST,OPTIONS';
add_header 'Access-Control-Allow-Headers' 'Content-Type';
}
}
|
此时前端请求地址改成同源:
1
|
fetch('/api/user') // 实际访问 http://localhost/api/user → 被Nginx转发
|
(五) iframe + postMessage
在某些场景下,可以通过iframe和postMessage实现跨域通信。postMessage是一个可以安全地跨域传递消息的方法。通过在页面中嵌入iframe,父页面和子页面可以通过postMessage进行通信。
父页面代码示例:
1
2
3
4
5
|
<iframe id="myFrame" src="https://example.com"></iframe>
<script>
const iframe = document.getElementById('myFrame');
iframe.contentWindow.postMessage('Hello from parent', 'https://example.com');
</script>
|
子页面代码示例:
1
2
3
4
5
|
window.addEventListener('message', (event) => {
if (event.origin === 'https://parent.com') {
console.log('Received message:', event.data);
}
});
|
总结
跨域问题是前端开发中的常见挑战,了解并掌握不同的跨域解决方案能帮助你更高效地进行开发工作。CORS是最推荐的跨域解决方案,尤其是在RESTful API和现代Web应用中广泛应用。JSONP虽然也能解决跨域问题,但它只支持GET请求,且存在一些安全隐患。代理和Nginx反向代理是开发环境和生产环境中常用的跨域解决方案。iframe + postMessage则适用于嵌入外部内容并进行安全的跨域通信。选择合适的跨域解决方案非常重要。
链接
Spring Boot跨域问题终结者!3种配置方式让前后端联调不再头疼 : https://blog.51cto.com/jiangyi/14273042]
Spring Boot项目中解决跨域问题(四种方式): https://blog.csdn.net/2509_94202084/article/details/155424021