什么是跨域?什么情况会触发跨域,以及如何解决跨域?
什么是跨域?
跨域(Cross-Origin)是指在一个网页中,发起对不同源(域名、协议或端口不同)的资源请求。由于安全原因,浏览器默认会阻止这种跨域请求,这是因为同源策略(Same-Origin Policy)的限制。同源策略是浏览器的一种安全机制,用于防止不同来源的资源之间的恶意交互。
同源策略
同源策略要求:
- 协议相同(例如,都是HTTP或HTTPS)。
- 域名相同(例如,都是example.com)。
- 端口相同(例如,都是80端口或443端口)。
如果三个条件中有任何一个不满足,浏览器就会认为这是一个跨域请求,默认情况下会被阻止。
触发跨域的情况
跨域请求通常是由浏览器发起的,当网页尝试从一个不同的源(域名、协议或端口不同)请求资源时,就会触发跨域。以下是一些常见会触发跨域请求的情况:
-
不同域名:
// 当前网页在 example.com fetch('https://another-domain.com/api/data'); // 跨域请求
-
不同子域名:
// 当前网页在 sub.example.com fetch('https://api.example.com/data'); // 跨域请求
-
不同协议:
// 当前网页在 https://example.com fetch('http://example.com/api/data'); // http 与 https 不同
-
不同端口:
// 当前网页在 http://example.com:80 fetch('http://example.com:8080/api/data'); // 端口 80 与 8080 不同
如何解决跨域?
为了在合法的情况下允许跨域请求,有多种解决方法:
1. 使用JSONP(JSON with Padding)
JSONP是一种传统的解决跨域请求的方法,通过动态插入<script>
标签来实现跨域请求。它仅支持GET请求。
<script>
function handleResponse(data) {
console.log(data);
}
var script = document.createElement('script');
script.src = 'https://example.com/data?callback=handleResponse';
document.body.appendChild(script);
</script>
2. 使用CORS(Cross-Origin Resource Sharing)
CORS是W3C标准,是一种更现代和强大的解决跨域请求的方法。服务器通过设置HTTP头来允许跨域请求。
Access-Control-Allow-Origin: *
Access-Control-Allow-Methods: GET, POST, PUT, DELETE
Access-Control-Allow-Headers: Content-Type
在服务器端,可以通过编程来设置这些头。例如,在Node.js和Express中:
const express = require('express');
const app = express();
app.use((req, res, next) => {
res.header('Access-Control-Allow-Origin', '*');
res.header('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE');
res.header('Access-Control-Allow-Headers', 'Content-Type');
next();
});
app.get('/data', (req, res) => {
res.json({ message: 'This is CORS-enabled for all origins!' });
});
app.listen(3000, () => {
console.log('Server is running on port 3000');
});
3. 使用服务器代理
通过在同源服务器上设置一个代理服务器,由代理服务器去请求目标服务器,从而避免跨域问题。
在Node.js中,可以使用http-proxy-middleware:
const { createProxyMiddleware } = require('http-proxy-middleware');
app.use('/api', createProxyMiddleware({
target: 'https://example.com',
changeOrigin: true,
}));
4. 使用Iframe + PostMessage
通过嵌入iframe并使用postMessage
在父窗口和子窗口之间进行通信。
<!-- Parent Page -->
<iframe id="myFrame" src="https://example.com"></iframe>
<script>
var frame = document.getElementById('myFrame');
window.addEventListener('message', function(event) {
console.log('Message received:', event.data);
});
frame.contentWindow.postMessage('Hello from parent', 'https://example.com');
</script>
Spring中解决跨域
在Spring开发的服务端API中,默认情况下会遵循同源策略,不会自动允许跨域请求。为了允许跨域请求,需要显式地配置CORS。
1. 使用全局CORS配置
可以通过在配置类中使用@Configuration
和WebMvcConfigurer
接口来设置全局的CORS配置。
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 {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**") // 允许所有路径
.allowedOrigins("*") // 允许所有来源
.allowedMethods("GET", "POST", "PUT, DELETE, OPTIONS") // 允许的方法
.allowedHeaders("*") // 允许的请求头
.allowCredentials(true) // 允许携带凭证
.maxAge(3600); // 预检请求的缓存时间
}
}
2. 使用注解配置CORS
可以在控制器方法或类上使用@CrossOrigin
注解来设置CORS配置。
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@CrossOrigin(origins = "http://example.com") // 允许来自 http://example.com 的请求
@RestController
public class MyController {
@GetMapping("/api/data")
public String getData() {
return "Hello, World!";
}
}
3. 使用过滤器配置CORS
可以通过自定义过滤器来配置CORS,这种方式可以提供更灵活的控制。
import org.springframework.stereotype.Component;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
@Component
public class SimpleCORSFilter implements Filter {
@Override
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
HttpServletResponse response = (HttpServletResponse) res;
response.setHeader("Access-Control-Allow-Origin", "*");
response.setHeader("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS");
response.setHeader("Access-Control-Allow-Headers", "Content-Type");
response.setHeader("Access-Control-Allow-Credentials", "true");
chain.doFilter(req, res);
}
@Override
public void init(FilterConfig filterConfig) {
}
@Override
public void destroy() {
}
}
总结
跨域请求是由浏览器的同源策略引起的,通过配置CORS,可以在合法的情况下允许跨域请求。在实际应用中,根据具体需求选择合适的配置方法是非常重要的。例如,如果需要全局的CORS配置,使用WebMvcConfigurer
接口会更为简便;如果只需要对某些特定的控制器或方法进行配置,可以使用@CrossOrigin
注解。通过这些方法,可以确保在多源环境下的数据请求安全、可靠。