如何解决跨域问题?

跨域是指浏览器允许向服务器发送跨域请求,从而克服Ajax只能同源使用的限制。所以解决跨域问题还是无法避免的。

首先了解一下浏览器的同源策略/SOP(Same origin policy),它是浏览器最核心也最基本的安全功能。

所谓同源是指 "协议+域名+端口"三者相同,即便两个不同的域名指向同一个ip地址,也非同源。限制了不同源之间资源的交互。

并且,跨域并不是请求发不出去,而是请求发出去了,也正常返回结果了,但是结果被浏览器拦截了

所显示的跨域交互包括:

  • 无法读取非同源网页的 Cookie、LocalStorage 和 IndexedDB
  • 无法接触非同源网页的 DOM
  • 无法向非同源地址发送 AJAX 请求

1. 通过jsonp跨域

jsonp的原理就是利用<script>标签没有跨域限制,通过<script>标签的src属性,发送带有callback参数的GET请求,服务端将接口返回数据拼凑到callback函数中,返回给浏览器,浏览器解析执行,从而前端拿到callback函数返回的数据。

jsonp缺点:只能发送GET一种请求

js 复制代码
function jsonp(url, params, callback) {
  // 生成唯一的回调函数名
  const callbackName = 'jsonp_' + Date.now();

  // 将参数拼接到 URL 中
  const queryString = Object.keys(params)
    .map(key => encodeURIComponent(key) + '=' + encodeURIComponent(params[key]))
    .join('&');

  // 创建 script 元素
  const script = document.createElement('script');
  script.src = url + '?' + queryString + '&callback=' + callbackName;

  // 定义回调函数,服务器返回数据后被调用
  window[callbackName] = function(data) {
    // 调用回调函数
    callback(data);

    // 删除 script 元素和回调函数
    document.head.removeChild(script);
    delete window[callbackName];
  };

  // 将 script 元素添加到页面中
  document.head.appendChild(script);
}

使用示例:

js 复制代码
jsonp('http://www.example.com/api', { user: 'admin' }, function(data) {
  console.log(data);
});

这个 jsonp 函数接受三个参数:URL、参数对象和回调函数。它会生成一个唯一的回调函数名,并将参数拼接到 URL 中。然后创建一个 <script> 元素,并将 URL 设置为带有回调函数名的 URL。定义一个全局的回调函数,当响应返回时调用该回调函数,并将数据传递给回调函数。最后将 <script> 元素添加到页面中,触发跨域请求。当请求完成后,删除 <script> 元素和回调函数。

2. document.domain + iframe跨域

此方案仅限主域相同,子域不同的跨域应用场景
这两个域名必须属于同一个一级域名!而且所用的协议,端口都要一致,否则无法利用document.domain进行跨域。

主域名又称一级域名或者顶级域名,由域名主体.域名后缀组成,比如domain.com

子域名有二级域名,比如www.domain.com。 三级域名,比如home.m.domain.com

实现原理:两个页面都通过js强制设置document.domain为基础主域,就实现了同域。

1) 父窗口(www.domain.com/a.html)

html 复制代码
<iframe id="iframe" src="http://child.domain.com/b.html"></iframe>
<script>
    document.domain = 'domain.com';
    var user = 'admin';
</script>
  1. 子窗口(child.domain.com/b.html)
js 复制代码
document.domain = 'domain.com';
// 获取父窗口中的变量
alert('get js data from parent ---> ' + window.parent.user);

3. nginx代理跨域

通过 Nginx 配置反向代理,将跨域请求转发到同源接口,从而避免浏览器的同源策略限制。

ini 复制代码
# proxy服务器
server {
  listen 80;
  server_name your-domain.com;

  location /api {
    # 设置代理目标地址
    proxy_pass http://api.example.com;
    
    # 设置允许的跨域请求头
    add_header Access-Control-Allow-Origin $http_origin;
    add_header Access-Control-Allow-Methods "GET, POST, OPTIONS";
    add_header Access-Control-Allow-Credentials true;
    add_header Access-Control-Allow-Headers "Origin, X-Requested-With, Content-Type, Accept";
    
    # 处理预检请求(OPTIONS 请求)
    if ($request_method = OPTIONS) {
      return 200;
    }
  }
}

在上面的示例中,假设你的域名是 your-domain.com,需要代理访问 api.example.com。你可以将这个配置添加到 Nginx 的配置文件中。

这个配置会将 /api 路径下的请求代理到 http://api.example.com。同时,通过添加 Access-Control-Allow-* 头部,允许跨域请求的来源、方法、头部等。

这样,当你在前端发送请求到 /api 路径时,Nginx 会将请求代理到 http://api.example.com,并在响应中添加跨域相关的头部,从而解决跨域问题。注意要根据实际情况进行配置,包括监听的端口、域名和代理的目标地址等。

4. nodejs中间件代理跨域

使用 Node.js 构建一个中间件,在服务器端代理请求,将跨域请求转发到同源接口,然后将响应返回给前端。

可以使用 http-proxy-middleware 模块来创建一个简单的代理服务器

js 复制代码
const express = require('express');
const { createProxyMiddleware } = require('http-proxy-middleware');

const app = express();

// 创建代理中间件
const apiProxy = createProxyMiddleware('/api', {
  target: 'http://api.example.com', // 设置代理目标地址
  changeOrigin: true, // 修改请求头中的 Origin 为目标地址
  pathRewrite: {
    '^/api': '', // 重写请求路径,去掉 '/api' 前缀
  },
  // 可选的其他配置项...
});

// 将代理中间件应用到 '/api' 路径
app.use('/api', apiProxy);

// 启动服务器
app.listen(3000, () => {
  console.log('Proxy server is running on port 3000');
});

在上面的示例中,首先使用 express 框架创建一个服务器实例。然后,使用 http-proxy-middleware 模块创建一个代理中间件。通过配置代理中间件的 target 选项,将请求代理到目标地址 http://api.example.com

你可以通过其他可选的配置项来进行更多的定制,例如修改请求头、重写请求路径等。在这个示例中,我们将代理中间件应用到路径 /api 下,即当请求路径以 /api 开头时,会被代理到目标地址。

最后,启动服务器并监听指定的端口(这里是 3000)。

请确保你已经安装了 expresshttp-proxy-middleware 模块,并将上述代码保存为一个文件(例如 proxy-server.js)。然后通过运行 node proxy-server.js 来启动代理服务器。

现在,当你在前端发送请求到 /api 路径时,Node.js 代理服务器会将请求转发到 http://api.example.com,从而实现跨域访问。记得根据实际情况修改目标地址和端口号。

5. 通过webpack devserver代理

使用 webpack-dev-server 的代理功能可以实现在开发过程中的跨域请求。配置 devServer 对象中的 proxy 选项来设置代理。

js 复制代码
module.exports = {
  // 其他配置项...
  devServer: {
    proxy: {
      '/api': {
        target: 'http://api.example.com', // 设置代理目标地址
        pathRewrite: { '^/api': '' }, // 重写请求路径,去掉 '/api' 前缀
        changeOrigin: true, // 修改请求头中的 Origin 为目标地址
      },
    },
  },
};

在上面的示例中,我们配置了一个代理,将以 /api 开头的请求转发到 http://api.example.com。通过 pathRewrite 选项,我们去掉了请求路径中的 /api 前缀,以符合目标地址的接口路径。

将上述配置添加到你的 webpack.config.js 文件中,然后启动 webpack-dev-server。现在,当在前端发送以 /api 开头的请求时,webpack-dev-server 会将请求转发到目标地址,并返回响应结果。

注意,这里的配置是针对开发环境下的代理,当构建生产环境的代码时,代理配置不会生效。

请确保已经安装了 webpack-dev-server,并在 package.json 文件的 scripts 中添加启动命令,例如:

json 复制代码
{
  "scripts": {
    "start": "webpack-dev-server --open"
  }
}

运行 npm startyarn start 来启动 webpack-dev-server

这样,通过配置 webpack-dev-server 的代理,就可以在开发过程中实现跨域请求。记得根据实际情况修改目标地址和请求路径。

6. CORS(跨域资源共享)

在服务端设置响应头部,允许特定的域名或所有域名访问该资源。可以通过在响应头部中设置 Access-Control-Allow-Origin 字段来指定允许访问的域名。

CORS请求设置的响应头字段,都以 Access-Control-开头:

1)Access-Control-Allow-Origin :必选

它的值要么是请求时Origin字段的值,要么是一个*,表示接受任意域名的请求。

2)Access-Control-Allow-Credentials :可选

它的值是一个布尔值,表示是否允许发送Cookie。默认情况下,Cookie不包括在CORS请求之中。设为true,即表示服务器明确许可,Cookie可以包含在请求中,一起发给服务器。这个值也只能设为true,如果服务器不要浏览器发送Cookie,删除该字段即可。

3)Access-Control-Expose-Headers :可选

CORS请求时,XMLHttpRequest对象的getResponseHeader()方法只能拿到6个基本字段:Cache-Control、Content-Language、Content-Type、Expires、Last-Modified、Pragma。如果想拿到其他字段,就必须在Access-Control-Expose-Headers里面指定。上面的例子指定,getResponseHeader('FooBar')可以返回FooBar字段的值。

js 复制代码
const express = require('express');
const app = express();

// 允许所有域名访问
app.use((req, res, next) => {
  res.setHeader('Access-Control-Allow-Origin', '*');
  next();
});

// 路由和处理逻辑
// ...

app.listen(3000, () => {
  console.log('Server is running on port 3000');
});

7. WebSocket

使用 WebSocket 协议进行通信,WebSocket 不受同源策略限制,因此可以在不同域之间进行双向通信。

利用webSocket的API,可以直接new一个socket实例,然后通过open方法内send要传输到后台的值,也可以利用message方法接收后台传来的数据。后台是通过new WebSocket.Server({port:3000})实例,利用message接收数据,利用send向客户端发送数据。

js 复制代码
const socket = new WebSocket('ws://example.com/socket');

socket.onopen = () => {
  console.log('WebSocket connection established.');
  // 发送数据
  socket.send('Hello, server!');
};

socket.onmessage = (event) => {
  console.log('Received message from server:', event.data);
};

socket.onclose = () => {
  console.log('WebSocket connection closed.');
};

原生WebSocket API使用起来不太方便,我们可以使用Socket.io,它很好地封装了webSocket接口,提供了更简单、灵活的接口,也对不支持webSocket的浏览器提供了向下兼容。

相关推荐
passerby60614 分钟前
完成前端时间处理的另一块版图
前端·github·web components
掘了12 分钟前
「2025 年终总结」在所有失去的人中,我最怀念我自己
前端·后端·年终总结
崔庆才丨静觅15 分钟前
实用免费的 Short URL 短链接 API 对接说明
前端
崔庆才丨静觅36 分钟前
5分钟快速搭建 AI 平台并用它赚钱!
前端
崔庆才丨静觅1 小时前
比官方便宜一半以上!Midjourney API 申请及使用
前端
Moment1 小时前
富文本编辑器在 AI 时代为什么这么受欢迎
前端·javascript·后端
崔庆才丨静觅1 小时前
刷屏全网的“nano-banana”API接入指南!0.1元/张量产高清创意图,开发者必藏
前端
剪刀石头布啊1 小时前
jwt介绍
前端
爱敲代码的小鱼1 小时前
AJAX(异步交互的技术来实现从服务端中获取数据):
前端·javascript·ajax
Cobyte2 小时前
AI全栈实战:使用 Python+LangChain+Vue3 构建一个 LLM 聊天应用
前端·后端·aigc