前后端分离实战:跨域问题及其有效解决方案

​

在现代Web开发中,特别是随着前后端分离架构的普及,跨域问题成为了开发者必须面对的一个重要议题。本文将详细介绍什么是跨域问题、其产生的原因以及如何从前端和后端两个角度来解决这个问题,并提供一些实用的代码示例。

一、跨域问题概述

1. 定义

跨域问题是指当一个资源试图从一个源加载时,如果该资源的域名、协议或端口号与当前网页的域名、协议或端口号不同,则会被浏览器阻止访问。这是为了防止恶意网站读取另一个网站的数据,从而保护用户的隐私和安全。

2. 常见场景

  • 前端页面部署在一个服务器上,而后端API部署在另一个服务器。
  • 开发环境与生产环境使用不同的域名或端口。

例如:

  • https://example.com/api 与 https://example.com:8080/api 不同源(端口不同)
  • https://example.com/api 与 http://example.com/api 不同源(协议不同)
  • https://example.com/api 与 https://sub.example.com/api 不同源(域名不同)

二、跨域问题产生原因

1. 同源策略

浏览器遵循同源策略(Same-origin policy),即只有当请求的URL的协议、域名和端口号都相同的情况下,才允许获取资源。任何一项不匹配都会导致跨域问题。

2. 预检请求(Preflight Request)

对于非简单请求(如PUT, DELETE等),浏览器会先发送一个OPTIONS请求到目标服务器,询问服务器是否允许此次跨域请求。如果服务器响应正确,浏览器才会继续发送实际请求。

三、解决方案

(一)前端解决方案

1. Proxy代理

在开发环境中,可以使用前端工具(如 Webpack Dev Server、Vite)配置代理服务器,将跨域请求转发到目标服务器。

示例(Vue.js/Vite 配置):

javascript 复制代码
// vite.config.js
export default defineConfig({
  server: {
    proxy: {
      '/api': {
        target: 'http://backend-api.com',  // 后端API地址
        changeOrigin: true,
        rewrite: (path) => path.replace(/^/api/, '')
      }
    }
  }
})

原理:

前端请求发送到同域名的代理服务器(如http://localhost:3000/api),代理服务器再将请求转发到实际的后端服务器(如http://backend-api.com),从而避免浏览器的同源策略限制。

2. JSONP

JSONP 是一种古老的跨域解决方案,利用了<script>标签不受同源策略限制的特性。

工作流程:

  1. 前端动态创建<script>标签,src 指向后端 API,并添加回调函数名作为参数(如callback=handleData)
  2. 后端收到请求后,将 JSON 数据包装在回调函数中返回(如handleData({"name":"John"}))
  3. 浏览器执行返回的 JavaScript 代码,触发回调函数处理数据

示例代码:

javascript 复制代码
function loadData() {
  const script = document.createElement('script');
  script.src = 'http://api.example.com/data?callback=handleData';
  document.body.appendChild(script);
}

function handleData(data) {
  console.log('Received data:', data);
}

缺点

  • 只支持 GET 请求
  • 安全性较低,容易受到 XSS 攻击
  • 仅适用于与支持 JSONP 的 API 交互

(二)后端解决方案

1. CORS(Cross-Origin Resource Sharing)

CORS是一种W3C标准,它允许服务器声明哪些源站通过浏览器有权限访问哪些资源。下面是一个基于Gin框架的Go语言实现示例:

go 复制代码
package middlewares

import (
	"net/http"
	"github.com/gin-gonic/gin"
)

func Cors() gin.HandlerFunc {
	return func(context *gin.Context) {
		method := context.Request.Method

		// 允许所有域名进行跨域调用
		context.Header("Access-Control-Allow-Origin", "*")
		// 允许任何请求头
		context.Header("Access-Control-Allow-Headers", "Content-Type,AccessToken,X-CSRF-Token, Authorization, Token,x-token,X-User-Id")
		// 允许任何方法(POST、GET等)
		context.Header("Access-Control-Allow-Methods", "POST, GET, OPTIONS, PUT, DELETE,UPDATE")
		// 允许浏览器解析的头
		context.Header("Access-Control-Expose-Headers", "Content-Length, Access-Control-Allow-Origin, Access-Control-Allow-Headers, Content-Type")
		// 允许携带Cookie
		context.Header("Access-Control-Allow-Credentials", "true")

		// 处理预检请求
		if method == "OPTIONS" {
			context.AbortWithStatus(http.StatusNoContent)
		}

		// 继续处理请求
		context.Next()
	}
}

关键响应头说明:

  • Access-Control-Allow-Origin:指定允许访问资源的源,可以是具体域名或*(允许所有)
  • Access-Control-Allow-Methods:允许的 HTTP 方法
  • Access-Control-Allow-Headers:允许的请求头
  • Access-Control-Allow-Credentials:是否允许携带 Cookie
  • Access-Control-Max-Age:预检请求的缓存时间

注意事项:

  • 生产环境应避免使用*,而是指定具体的域名
  • 如果设置了Access-Control-Allow-Credentials: true,则不能使用*
  • 预检请求(OPTIONS)需要快速响应,通常返回 204 状态码

四、生产环境中的跨域配置建议

  1. 精细控制 CORS 设置:

    • 避免使用Access-Control-Allow-Origin: *,应指定具体的前端域名
    • 严格限制允许的请求头和方法
    • 仅在必要时启用Access-Control-Allow-Credentials
  2. 使用 HTTPS:混合使用 HTTP 和 HTTPS 可能导致跨域问题,建议前后端均使用 HTTPS。

  3. 监控预检请求:确保服务器正确处理 OPTIONS 请求,避免性能瓶颈。

  4. 考虑 CDN:静态资源(如 CSS、JS、图片)可以部署到 CDN,避免跨域问题。

五、总结

跨域问题是Web开发中不可避免的一部分,尤其是在前后端分离的趋势下。了解其背后的原理有助于我们选择合适的解决方案。一般来说,后端通过配置CORS是最直接有效的方式,而前端则可以通过代理或者JSONP等方式作为补充。合理地应用这些技术,可以有效地提升用户体验,同时确保系统的安全性。

如果这篇文章对大家有帮助可以点赞关注,你的支持就是我的动力😊!

​

相关推荐
海宇服务25 分钟前
零信任架构实战:基于海宇风控经营异常预警构建自动化央企物资采购供应商资信排查网关
运维·人工智能·架构·自动化
加宝的起点雷达1 小时前
多个 AI 编程代理之间,上下文怎么共享:三类做法和三个坑
人工智能·架构
weixin_750330233 小时前
GEO获客与电商短视频:AI销售架构的选型分析
人工智能·架构·电商带货短视频·geo搜索排名
我的div丢了肿么办4 小时前
go语言对路由中的业务逻辑进行抽离
后端·go
leo在掘金4 小时前
纯Rust重写Photoshop,一个月7.2万Star
后端·架构
HelloWorld0014 小时前
百万 QPS 流量压测不垮!基于 Spring Cloud Gateway + Sentinel 打造分布式动态限流与灰度分流中枢(源码+避坑实战)
架构
王中阳Go4 小时前
网页上答对、接口里答错:同一个知识库,差的不是模型是入口
人工智能·后端·架构
beibeix20155 小时前
hiredis 架构与功能分析
架构
利威尔xu5 小时前
Go IM 源码实战(2):WebSocket 握手 11 步,少 1 个参数连不上
后端·websocket·微服务·面试·golang
淡海水5 小时前
07-01-源码-.NET Core GC总览与架构
unity·架构·c#·.netcore·gc