跨域是什么 有什么影响 怎么解决

0 阅读7分钟

 

一、什么是跨域

跨域 = 浏览器的 “同源策略” 限制

前端页面所在的源请求接口的源不一致时,浏览器会判定为跨域。

这里的 源(Origin) 由三部分组成:

协议 + 域名 / 主机 + 端口

只要任意一项不同,就是跨域。

举例判断

页面地址:http://localhost:8080/index.html

请求地址是否跨域原因
http://localhost:8080/api完全同源
https://localhost:8080/api协议不同(http/https)
http://127.0.0.1:8080/api主机不同
http://localhost:8081/api端口不同
api.xxx.com/api域名不同

一句话:浏览器认为:不是同一个 “网站”,就不能随便拿数据。


二、跨域是谁造成的?为什么会有?

1. 谁造成的?

完全是浏览器造成的,和服务器、网络、Java 代码无关。

  • 服务器收到请求了 
  • 业务逻辑执行了 
  • 数据库改了 
  • 响应也返回了 

浏览器:拦截响应 → 不给 JS → 控制台报错

给个一个真实的危险场景:

  1. 你登录了网上银行 bank.com,浏览器存了你的登录 Cookie。
  2. 你手贱点了一个恶意网站 hack.com
  3. 恶意网站的 JS 偷偷给 bank.com/transfer 发了一个转账请求,把你的钱转到黑客账户。

如果没有跨域限制:

  • 银行服务器会收到请求 
  • 会执行转账逻辑 
  • 会扣你的钱 
  • 会返回「转账成功」的响应 
  • 恶意网站的 JS 能拿到这个响应,知道钱转成了 

有了跨域限制:

  • 银行服务器还是收到了请求 
  • 还是执行了转账 
  • 还是扣了你的钱 
  • 还是返回了「成功」的响应 
  • 但是!浏览器发现:这个请求是 hack.com 发的,不是 bank.com 发的!
  • 浏览器直接把「成功」的响应扣下来,不给 hack.com 的 JS,还在控制台报错。

注意:钱还是被扣了! 跨域只能防止黑客拿到你的数据,不能防止黑客发请求。这就是为什么还要有 CSRF Token 等防护。

那问题来了:钱已经被扣了,怎么解决?

核心思路:让服务器能识别出「这个请求是正常用户发的,还是恶意网站伪造的」,从根源上拒绝伪造的请求。这就是 CSRF Token(跨站请求伪造防护令牌) 要解决的问题。


CSRF Token 是什么?原理是什么?

  1. 核心定义

CSRF Token 是服务端生成的、随机、唯一、不可预测的令牌,绑定在用户的会话中,只有「正常访问银行页面的用户」能拿到,恶意网站完全拿不到。

  1. 工作原理(用银行转账举例)

正常用户的流程(合法请求)

  1. 用户登录 bank.com,服务端生成一个随机的 CSRF Token=abc123xyz,存在用户的 Session 中

  2. 服务端把这个 Token嵌入到转账页面的 HTML 里(比如隐藏表单、请求头)

    <form action="/transfer" method="post">
        <input type="hidden" name="csrfToken" value="abc123xyz">
        <input type="text" name="toAccount">
        <input type="submit" value="转账">
    </form>
    

  3. 用户点击「转账」时,浏览器会把 csrfToken=abc123xyz 一起发给银行服务器

  4. 服务器校验:请求里的 Token 和 Session 里存的 Token 完全一致 → 验证通过,执行转账

恶意网站的攻击流程(伪造请求)

  1. 用户打开 hack.com,恶意网站想发起转账请求

  2. hack.com 是跨站,完全无法读取 bank.com 页面里的 CSRF Token(同源策略限制)

  3. 恶意网站发起请求时,只能构造转账参数,带不上正确的 CSRF Token

  4. 银行服务器收到请求,校验 Token:请求里没有 Token/Token 错误 → 直接拒绝,转账失败!

  5. 核心逻辑

CSRF 攻击的本质是「冒充用户发请求,但拿不到用户页面里的敏感信息」。CSRF Token 就是利用这一点:把「请求的合法性」和「用户是否正常访问页面」绑定,只有正常访问页面的用户能拿到 Token,恶意网站拿不到,自然无法构造合法请求。

2. 为什么浏览器要这么做?(安全本质)

为了防止 CSRF 攻击(跨站请求伪造)

你登录了银行网站 bank.com,浏览器保存了它的 Cookie。你打开恶意网站 hack.com。恶意网站 JS 发送请求:

GET https://bank.com/user/info

浏览器会自动带上 bank.com 的 Cookie

如果没有跨域限制:恶意 JS 直接拿到你的账户、余额、交易记录。

同源策略 = 浏览器保护用户 Cookie 不被第三方网站盗用。


三、跨域会带来什么影响?

1. 前端表现

  • 控制台报错:

    Access to XMLHttpRequest at 'xxx' from origin 'xxx' has been blocked by CORS policy
    

  • 请求状态码可能是 200,但拿不到数据

  • 页面无法渲染、接口调用失败

2. 真实网络行为

  • 请求成功到达后端
  • 后端正常处理
  • 响应成功回到浏览器
  • 浏览器拦截数据,不交给 JS

3. 附带影响

  • 跨域时 Cookie 默认不携带
  • 自定义请求头(如 Authorization、Token)会被拦截
  • PUT/DELETE/JSON 请求会触发 OPTIONS 预检

四、通用解决方案(原理级)

方案 1:CORS(跨域资源共享)

浏览器与服务器通过 HTTP 头协商:

  • 请求头:Origin 标明来源
  • 响应头:Access-Control-Allow-* 标明允许谁访问

浏览器检查通过 → 放行数据。

方案 2:Nginx 反向代理

让前端和接口看起来同源,浏览器就不限制。

方案 3:网关统一处理(Spring Cloud Gateway)

在网关层统一加 CORS 头,后端服务无感。

方案 4:JSONP(淘汰)

只支持 GET,利用 <script> 标签无跨域限制,不安全、不推荐。


五、生产级解决方案


方案 1:Nginx 反向代理(最推荐、最安全、生产首选)

原理

前端访问:https://www.xxx.com/api/xxxNginx 把 /api/** 转发到后端服务。

对浏览器来说:始终同源,不存在跨域。

Nginx 配置

server {
    listen 443 ssl;
    server_name www.xxx.com;

    # 前端静态资源
    location / {
        root /dist;
        index index.html;
        try_files $uri $uri/ /index.html;
    }

    # 后端接口代理
    location /api/ {
        proxy_pass http://backend-server:8080/;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    }
}

优点

  • 彻底消灭跨域
  • 安全、统一入口
  • 适合微服务、前后端分离
  • 金融 / 银行项目标准方案

方案 2:Spring Boot 全局 CORS 配置(可上生产)

适用场景

  • 内部系统
  • 对外 API 服务
  • 没有 Nginx 统一入口

生产级代码

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.CorsConfigurationSource;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import org.springframework.web.filter.CorsFilter;

import java.util.Arrays;
import java.util.Collections;

@Configuration
public class CorsConfig {

    @Bean
    public CorsFilter corsFilter() {
        CorsConfiguration config = new CorsConfiguration();

        // 生产环境必须写真实前端域名,严禁 *
        config.setAllowedOrigins(Arrays.asList(
                "https://www.xxx.com",
                "https://admin.xxx.com"
        ));

        // 允许携带 Cookie
        config.setAllowCredentials(true);

        // 允许所有请求头
        config.addAllowedHeader("*");

        // 允许所有方法
        config.addAllowedMethod("*");

        // 暴露自定义响应头(前端可读取)
        config.addExposedHeader("Authorization");

        // 预检缓存 1 小时,减少 OPTIONS 请求
        config.setMaxAge(3600L);

        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        source.registerCorsConfiguration("/**", config);

        return new CorsFilter(source);
    }
}

生产严禁事项

  • AllowedOrigin 绝对不能用 *
  • 必须开启 AllowCredentials: true 才能带 Cookie
  • 暴露头必须显式声明,否则前端读不到

方案 3:Spring Cloud Gateway 统一跨域(微服务标准)

@Configuration
public class GatewayCorsConfig {
    @Bean
    public CorsWebFilter corsWebFilter() {
        CorsConfiguration config = new CorsConfiguration();
        config.setAllowedOrigins(Arrays.asList("https://www.xxx.com"));
        config.setAllowCredentials(true);
        config.addAllowedHeader("*");
        config.addAllowedMethod("*");
        config.setMaxAge(3600L);

        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(new PathPatternParser());
        source.registerCorsConfiguration("/**", config);

        return new CorsWebFilter(source);
    }
}


六、前端配合(Axios)

如果你要跨域携带 Cookie / Token

axios.create({
  withCredentials: true,  // 跨域带 Cookie
  headers: {
    'Content-Type': 'application/json'
  }
})


七、总结

  1. 跨域是浏览器的同源策略限制,不是服务器限制。
  2. 请求能发出去、后端能执行,只是响应被浏览器拦截。
  3. 目的是防止第三方网站盗用你的 Cookie 做 CSRF。
  4. 生产最优解:Nginx 反向代理,无跨域、最安全。
  5. 次优解:后端配置 CORS,严格限制来源域名。