1.什么是跨域
跨域问题的实质是浏览器的同源策略造成的。浏览器同源策略是浏览器为 JavaScript
施加的限制。简单点说就是非同源会出现如下等限制:
- 无法访问其他源下的网页的 Cookies,Storage等;
- 无法访问其他源下的DOM对象和 JS 对象;
- 无法使用 Ajax 向其他源发送请求(除非其他源允许)
那什么情况下会出现限制呢?也就是什么时候会出现跨域?
前提:必须运行在浏览器中。因为同源策略是浏览器对 JS 施加的
- 协议不同。http://abc.com:80 的网页访问 https://abc.com:80
- 主机名不同。https://abc.com:80 的网页访问 https://cde.com:80 / https://api.abc.com:80 (子域名也不行)
- 端口不同。https://abc.com:80 的网页访问 https://abc.com:443
举个栗子
前端运行地址:127.0.0.1:3000
js
import React, { useState } from "react";
const App = () => {
const [data, setData] = useState(null);
const fetchData = async () => {
try {
const response = await fetch('http://127.0.0.1:8080/test');
const data = await response.json();
setData(data);
} catch (error) {
console.error('Error:', error);
}
};
return (
<div>
<button onClick={fetchData}>Fetch</button>
{data && <pre>{JSON.stringify(data, null, 2)}</pre>}
</div>
);
};
export default App;
后端运行地址:127.0.0.1:8080
java
@RestController
public class TestController {
@GetMapping("/test")
public Map<String, Object> test(HttpServletResponse response) {
return new HashMap<String, Object>() {{
put("ok" , true);
}};
}
}
点击 Fetch 后,产生跨域
2.为什么要有同源策略
Web的同源策略是一种浏览器安全机制,被设计用于保护用户信息和防止恶意攻击。同源策略要求浏览器在加载网页时,只允许与当前页面具有相同源的资源进行交互,即协议、域名和端口必须完全相同。
同源策略的主要目的是防止恶意网站通过跨站脚本攻击(XSS)或跨站请求伪造(CSRF)等方式获取用户的敏感信息或进行恶意操作。
具体原因如下:
-
防止信息泄露:同源策略阻止恶意的网站访问其他域名下的数据和文档对象模型(DOM)。这是因为在不同域下的网页很可能具有不同的安全性控制政策和访问权限,所以限制跨域访问有助于保护用户的敏感信息,如登录凭据、Cookie、本地存储等。
-
数据隔离:同源策略确保网站之间的数据相互隔离,防止一个网站的恶意代码或攻击不会影响其他网站的数据完整性和可用性。此举有助于防止一些常见的攻击,如点击劫持和防止恶意脚本对其他网站进行操作。
-
安全性和可靠性:同源策略有助于维护浏览器的安全性和稳定性。通过限制跨域访问,它可以减少恶意代码的传播,并减少对浏览器的影响。
虽然同源策略在保护用户信息和防止恶意攻击方面非常重要,但有时也会带来一定的限制。因此,如果确实需要在不同源之间进行数据交互,可以使用跨域资源共享(CORS)等安全机制来规避同源策略的限制。
(来自ChatGPT)
3.怎么解决跨域问题,即绕过同源策略限制
跨域实例中,前端和后端运行的端口不同(前端3000,后端8080)所以导致了跨域。如果想要前端能直接访问后端,有什么办法呢?
3.1 服务端设置CORS
这是最简单的办法,在后端加上配置允许所有的源地址访问:
"Access-Control-Allow-Origin" : "*"
java
@RestController
public class TestController {
@GetMapping("/test")
public Map<String, Object> test(HttpServletResponse response) {
// 这是最核心的配置,可以使用更加优雅的方式,@CrossOrigin注解、拦截器、过滤器等
response.setHeader("Access-Control-Allow-Origin" , "*");
return new HashMap<String, Object>() {{
put("ok" , true);
}};
}
}
3.2 代理
代理的核心是让浏览器页面访问一个同源的服务端,让同源服务端去获取页面想要的资源。
Nginx 中:
config
server {
listen 3000;
server_name 127.0.0.1;
location /api {
proxy_pass http://localhost:8080;
}
}
Node 中:(以 开发React 应用为例)
js
const proxy = require('http-proxy-middleware');
module.exports = function (app) {
app.use(
'/api',
proxy({
target : 'http://127.0.0.1:8080/',
changeOrigin : true,
PathRewrite : {
'^/api' : ''
}
})
);
};
3.3 JSONP
简单来说就是利用 HTML 标签来访问跨域的资源(因为同源策略是浏览器为 JavaScript
施加的限制)
前端准备好callback方法和请求的标签
html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<title>React App</title>
<script>
// 准备好回调的方法
const callback = (data) => {
console.log("data", data);
}
</script>
<!-- 通过访问接口,获取模拟的 data 入参 -->
<script src="http://127.0.0.1:8080/jsonp"></script>
</head>
<body>
<div id="root"></div>
</body>
</html>
后端增加接口,模拟返回 JS 脚本:
java
@GetMapping("/jsonp")
public String jsonp() {
return "callback({\"ok\": false});";
}