跨域
1. 什么是跨域?为什么会有跨域限制?
答案:跨域是指在浏览器中,当前页面的脚本试图访问不同源(协议、域名、端口三者有任一不同)的资源时受到的限制。
跨域限制的主要原因是安全考虑,它是浏览器的同源策略的一部分,用于防止恶意脚本访问其他网站的敏感数据。
2. 常见的跨域解决方案有哪些?
答案:常见的跨域解决方案包括:
- CORS(跨域资源共享)
- JSONP
- 代理服务器
- postMessage
- WebSocket
- document.domain + iframe
- 浏览器插件(如Chrome的Allow-Control-Allow-Origin)
3. 什么是CORS?如何实现CORS?
答案:CORS(Cross-Origin Resource Sharing,跨域资源共享)是一种基于HTTP头的机制,允许服务器声明哪些源站通过浏览器有权限访问哪些资源。
实现CORS的步骤:
- 服务器端设置响应头:
- Access-Control-Allow-Origin
- Access-Control-Allow-Methods
- Access-Control-Allow-Headers
- 对于非简单请求,服务器需要处理OPTIONS预检请求
- 客户端无需特殊设置,使用普通AJAX即可
示例(Node.js + Express):
javascript
app.use((req, res, next) => {
res.header('Access-Control-Allow-Origin', '*');
res.header('Access-Control-Allow-Methods', 'GET,PUT,POST,DELETE');
res.header('Access-Control-Allow-Headers', 'Content-Type');
next();
});
4. JSONP的原理是什么?如何实现JSONP?
答案:JSONP(JSON with Padding)利用了<script>
标签不受同源策略限制的特性。它通过动态创建script标签,向服务器请求数据,服务器返回一个函数调用,这个函数调用的参数就是我们需要的数据。
实现JSONP的步骤:
- 客户端定义一个回调函数
- 创建
<script>
标签,src指向接口地址,并带上回调函数名 - 服务器接收到请求后,将数据作为参数和函数名拼接成函数调用的形式返回
示例:
javascript
// 客户端
function handleResponse(data) {
console.log(data);
}
const script = document.createElement('script');
script.src = 'http://example.com/api?callback=handleResponse';
document.body.appendChild(script);
// 服务器端(Node.js)
app.get('/api', (req, res) => {
const data = {name: 'John', age: 30};
const callback = req.query.callback;
res.send(`${callback}(${JSON.stringify(data)})`);
});
5. 什么是预检请求(Preflight Request)?什么情况下会触发预检请求?
答案:预检请求是在某些跨域请求前,浏览器自动发起的 OPTIONS 请求,用于检查实际请求是否可以安全地发送。
触发预检请求的情况:
- 使用了以下HTTP方法之一:PUT、DELETE、CONNECT、OPTIONS、TRACE、PATCH
- 人为设置了以下集合之外首部字段:Accept、Accept-Language、Content-Language、Content-Type
- Content-Type 的值不属于以下之一:application/x-www-form-urlencoded、multipart/form-data、text/plain
6. 如何使用postMessage实现跨域通信?
答案:postMessage是HTML5引入的API,可以安全地实现跨源通信。
使用步骤:
- 在发送方使用
targetWindow.postMessage(message, targetOrigin)
发送消息 - 在接收方使用
window.addEventListener('message', handler)
监听消息
示例:
javascript
// 发送方(http://example.com)
const iframe = document.getElementById('myIframe');
iframe.contentWindow.postMessage('Hello from example.com', 'http://target.com');
// 接收方(http://target.com)
window.addEventListener('message', (event) => {
if (event.origin !== 'http://example.com') return;
console.log('Received message:', event.data);
});
7. 什么是CORS预检请求?如何优化CORS预检请求?
答案:CORS预检请求是一种OPTION请求,用于检查实际请求是否可以安全地发送。
优化CORS预检请求的方法:
- 使用Access-Control-Max-Age头部缓存预检请求结果
- 减少非简单请求:尽可能使用简单请求(GET、POST、HEAD)
- 合并多个请求以减少预检请求的次数
- 在服务器端正确处理OPTIONS请求
示例(设置预检请求缓存时间):
javascript
res.header('Access-Control-Max-Age', '86400'); // 缓存预检请求结果24小时
8. 如何在Node.js中实现CORS中间件?
答案:在Node.js中,可以使用Express框架并实现一个CORS中间件:
javascript
function corsMiddleware(req, res, next) {
res.header('Access-Control-Allow-Origin', '*');
res.header('Access-Control-Allow-Methods', 'GET,PUT,POST,DELETE,OPTIONS');
res.header('Access-Control-Allow-Headers', 'Content-Type, Authorization, Content-Length, X-Requested-With');
// 处理 OPTIONS 请求
if ('OPTIONS' === req.method) {
res.sendStatus(200);
} else {
next();
}
}
app.use(corsMiddleware);
9. 跨域资源共享(CORS)的安全隐患有哪些?如何防范?
答案:CORS的安全隐患主要包括:
- 过于宽松的Access-Control-Allow-Origin设置
- 错误的凭证处理
- 不安全的响应暴露
防范措施:
- 严格限制Access-Control-Allow-Origin,避免使用通配符 *
- 谨慎处理Access-Control-Allow-Credentials
- 使用Access-Control-Expose-Headers限制暴露的响应头
- 正确配置Access-Control-Allow-Methods和Access-Control-Allow-Headers
- 实施内容安全策略(CSP)
10. 什么是子域名跨域?如何解决子域名跨域问题?
答案:子域名跨域是指在同一主域名下的不同子域名之间的跨域问题,例如 sub1.example.com 和 sub2.example.com 之间的通信。
解决方法:
- 使用document.domain:javascript
document.domain = 'example.com';
- 使用postMessage
- 使用CORS
- 使用代理服务器
- 使用 jsonp
- 使用 window.name
注意:document.domain方法只适用于二级域名相同的情况,且存在安全风险,应谨慎使用。