彻底搞懂跨域:从定义到实战

4 阅读5分钟

在前端开发中,跨域是一个高频出现且容易踩坑的问题——很多开发者在调试接口时,会遇到浏览器控制台抛出的跨域报错,明明后端接口能正常返回数据,前端却无法获取。其实跨域并非后端接口故障,而是浏览器的专属安全限制。本文将从定义、根源、表现形式到实战示例,一步步拆解跨域,让你快速掌握核心逻辑,轻松应对开发中的跨域问题。

一、跨域的精准定义

很多人对跨域的理解存在偏差,认为只要是不同地址的请求就是跨域,其实不然。准确来说,跨域特指在浏览器端,一个源(Origin)的页面发起网络请求,访问了另一个不同源的服务器资源,这种请求行为才叫做跨域。

这里有一个关键前提必须牢记:跨域是浏览器独有的安全限制,服务器与服务器之间的请求(比如微服务间的调用、Java后端通过HttpClient发起的请求),完全不存在跨域问题,因为这类请求不经过浏览器的同源策略校验。

二、跨域的根源:浏览器的同源策略

跨域的出现,本质是浏览器的同源策略在起作用。同源策略是浏览器最基础的安全防线,设计初衷是为了保护前端页面的安全,防止恶意网站通过脚本窃取其他网站的敏感数据,比如用户的登录态Cookie、个人信息等。

1. 同源的判定标准(3个维度必须完全一致)

浏览器判断两个地址是否“同源”,有明确且严格的标准,必须同时满足协议、域名、端口三个维度完全相同,缺一不可。用公式可以简单表示为:


同源判定公式:协议://域名:端口 → 三者完全相等 = 同源,否则 = 不同源(跨域)
    

2. 同源与跨域的直观示例

我们以前端常用的本地地址http://localhost:8080 为例,通过几个具体示例,一眼就能区分同源和跨域,更易理解判定标准:

请求地址是否跨域原因分析
http://localhost:8080不跨域协议、域名、端口完全一致
https://localhost:8080跨域协议不同(http → https)
http://127.0.0.1:8080跨域域名不同(localhost≠127.0.0.1)
http://localhost:9090跨域端口不同(8080≠9090)
www.baidu.com跨域域名、端口均不同

三、跨域的核心表现形式

很多开发者遇到跨域时,会误以为是后端接口没通,其实不然。当浏览器发起跨域请求时,请求本身能正常到达后端服务器,后端也会正常处理并返回数据,但浏览器会在响应阶段拦截这份返回结果,并在控制台抛出跨域报错,导致前端无法获取到响应数据。

1. 典型跨域报错(浏览器控制台)

开发中最常见的跨域报错如下,只要出现类似日志,基本可以判定是跨域问题:


Access to XMLHttpRequest at 'http://localhost:9090/api/test' from origin 'http://localhost:8080' has been blocked by CORS policy: 
No 'Access-Control-Allow-Origin' header is present on the requested resource.
    

这里的关键的是:请求发出去了,数据也回来了,但被浏览器拦截了,这是跨域最核心的表现,也是区别于接口报错的关键特征。

2. 实战演示:模拟跨域场景

为了更直观地感受跨域现象,我们通过前后端简单代码,模拟一个跨域场景。

首先,编写后端接口(SpringBoot实现,端口9090):


package com.example.crossdomain.controller;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/api")
public class TestController {

    // 简单的测试接口,返回字符串
    @GetMapping("/test")
    public String testCrossDomain() {
        return "跨域测试:后端接口正常返回数据";
    }
}
    

配置后端端口为9090(application.properties):


server.port=9090
    

然后,编写前端页面(HTML+JS,运行在8080端口,可通过Tomcat或VS Code Live Server启动):


<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <title>跨域测试</title>
</head>
<body>
    <h3>跨域请求测试</h3>
    <button onclick="sendRequest()">发起跨域请求</button>
    <div id="result" style="margin-top: 20px;"></div>

    <script>
        // 发起请求到9090端口的接口(当前前端运行在8080端口,属于跨域)
        function sendRequest() {
            const xhr = new XMLHttpRequest();
            xhr.open("GET", "http://localhost:9090/api/test", true);
            xhr.onload = function() {
                // 若跨域成功,展示响应结果
                document.getElementById("result").innerText = xhr.responseText;
            };
            xhr.onerror = function() {
                // 跨域失败,提示错误
                document.getElementById("result").innerText = "请求失败,出现跨域问题";
            };
            xhr.send();
        }
    </script>
</body>
</html>
    

启动后端(9090端口)和前端(8080端口),点击“发起跨域请求”按钮,会发现前端无法展示后端返回的数据,打开浏览器控制台,会看到前面提到的跨域报错,这就是跨域的实际表现。

四、非同源但不跨域的特例

并非所有不同源的请求都会被浏览器拦截,浏览器对部分标签和资源豁免了同源策略,这类请求不会触发跨域,开发中常见的有以下几种:

  • 静态资源引入标签:
  • 表单提交: 提交数据,无论目标地址是否同源,都不会受到跨域限制。
  • 非浏览器请求:比如使用Postman工具发起请求、浏览器插件发起的请求,这些请求绕开了浏览器的同源策略校验,不会出现跨域问题。

五、一句话总结跨域核心

跨域是浏览器基于同源策略,对“不同源的前端→后端请求”做出的响应拦截行为,本质是浏览器的安全机制。想要解决跨域问题,核心就是让后端返回合规的CORS跨域响应头,告诉浏览器该请求是安全的,可以放行响应数据。