前端访问后端实现跨域

背景:前端在抖音里做了一个插件然后访问我们的后端。显然在抖音访问其他域名肯定会跨域。

解决办法:

1、使用比较简单的jsonp

JSONP

优点:JSONP 是通过动态创建 <script> 标签的方式加载外部数据,属于跨域数据请求的一种传统解决方案。JSONP 不会受到浏览器的同源策略限制,因此在某些跨域环境(如抖音小程序中)可以正常工作。

缺点:只能进行 GET 请求,无法支持 POST、PUT 等其他 HTTP 方法。

安全性较低,因为 JSONP 将代码直接注入到页面中,存在潜在的安全风险。

测试案例:

后端 springboot

java 复制代码
@RestController
@Slf4j
@RequestMapping("/api")
public class CrosController {

    @GetMapping("/author/info")
    public String crosTest(@RequestParam("authorName") String authorName,
                           @RequestParam(value = "callback", required = false) String callback){
        // 参数校验
        if (authorName == null) {
            return "Invalid request: authorName is required";
        }
        // 模拟返回数据
        String jsonData = "{\"author_id\": 12345, \"author_name\": \"" + authorName + "\"}";

        // 判断是否为 JSONP 请求
        if (callback != null && !callback.isEmpty()) {
            // JSONP 响应
            String jsonpResponse = callback + "(" + jsonData + ");";
            return jsonpResponse;
        } else {
            // 普通 JSON 响应
            return jsonData;
        }
    }

}

前端html页面

index.html 复制代码
<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
</head>

<body>

</body>

</html>

<script>
 function jsonpRequest(url, params, callbackName) {
  return new Promise((resolve, reject) => {
    // 动态创建回调函数名称
    const uniqueCallback = callbackName || `jsonpCallback_${Date.now()}`;

    // 将回调函数注册到全局对象中
    window[uniqueCallback] = function (response) {
      // 清理全局回调函数和 script 标签
      delete window[uniqueCallback];
      document.body.removeChild(script);

      // 返回响应数据
      resolve(response);
    };

    // 构建完整的 URL(拼接参数)
    const query = Object.entries(params)
      .map(([key, value]) => `${key}=${encodeURIComponent(value)}`)
      .join("&");
    const fullUrl = `${url}?${query}&callback=${uniqueCallback}`;

    // 创建 script 标签
    const script = document.createElement("script");
    script.src = fullUrl;

    // 处理加载失败
    script.onerror = function () {
      delete window[uniqueCallback];
      document.body.removeChild(script);
      reject(new Error("JSONP request failed"));
    };

    // 插入 script 标签到文档中
    document.body.appendChild(script);
  });
}

// 使用示例
const url = "http://localhost:8080/api/author/info";
const params = { authorName: "烟火中的水滴" };

jsonpRequest(url, params)
  .then((data) => {
    console.log("Author Info:", data);
  })
  .catch((error) => {
    console.error("Error:", error);
  });

</script>

然后打开次页面。

1、打开控制台 2、执行 可以看到结果正常返回了

java 复制代码
jsonpRequest(url, params)
  .then((data) => {
    console.log("Author Info:", data);
  })
  .catch((error) => {
    console.error("Error:", error);
  });

2、@CrossOrigin 解决跨域

Spring Boot 的 @CrossOrigin 注解用于配置 CORS(跨域资源共享),它允许你的后端接受来自其他域的请求。

优点:CORS 是更现代、更安全的跨域解决方案,相比 JSONP,支持所有 HTTP 方法(如 GET、POST 等)。安全性高,因为 CORS 会进行严格的域名、方法等验证。

缺点:如果客户端(如抖音小程序)对 CORS 的处理有限,可能无法支持复杂的预检请求(如 OPTIONS 请求)。CORS 依赖浏览器的支持,如果环境对跨域限制较为严格(如一些小程序环境),可能 CORS 不生效。

我们先开启后端运行起来。看一些问题

1、通过控制台源null模拟跨域

后端代码

java 复制代码
@RestController
@Slf4j
@RequestMapping("/api")
public class CrosController {
    @GetMapping("/author/info/cross")
    public String crossTest(@RequestParam("authorName") String authorName){
        // 参数校验
        if (authorName == null) {
            return "Invalid request: authorName is required";
        }
        // 模拟返回数据
        String jsonData = "{\"author_id\": 12345, \"author_name\": \"" + authorName + "\"}";
        return jsonData;
    }

}

1、通过curl

请求
curl 'http://localhost:8080/api/author/info/cross?authorName=123'

结果
{"author_id": 12345, "author_name": "123"}

这个属于正常的

2、打开一个index.html 然后控制台通过 前端代码访问这个接口

java 复制代码
fetch('http://localhost:8080/api/author/info?authorName=烟火中的水滴')
 .then(response => response.json())
 .then(data => console.log("JSON Response:", data))
 .catch(error => console.error("Error:", error));

发现报错

问题分析

错误提示:

No 'Access-Control-Allow-Origin' header is present on the requested resource 表示目标服务器(http://localhost:8080)没有返回允许跨域访问的CORS响应头。

浏览器的安全机制会拦截请求响应。

根本原因:

客户端代码运行的index.html被浏览器认为是从null源加载的(例如直接从文件系统打开),与http://localhost:8080是不同源。

目标服务器未配置CORS。
其实这个就是跨域了 因为你从一个控制台打开访问8080端口 但是你不是同源的就会报错。

那么解决办法就是加一个注解

java 复制代码
 @CrossOrigin(origins = "*") // 允许指定来源跨域
    @GetMapping("/author/info/cross")
    public String crossTest(@RequestParam("authorName") String authorName){
        // 参数校验
        if (authorName == null) {
            return "Invalid request: authorName is required";
        }
        // 模拟返回数据
        String jsonData = "{\"author_id\": 12345, \"author_name\": \"" + authorName + "\"}";
        return jsonData;
    }

重新访问
fetch('http://localhost:8080/api/author/info/cross?authorName=烟火中的水滴') .then(response => response.json()) .then(data => console.log("JSON Response:", data)) .catch(error => console.error("Error:", error))

成功了

我们看下他发起的curl请求参数:

java 复制代码
curl 'http://localhost:8080/api/author/info/cross?authorName=%E7%83%9F%E7%81%AB%E4%B8%AD%E7%9A%84%E6%B0%B4%E6%BB%B4' \
  -H 'Accept: */*' \
  -H 'Accept-Language: zh-CN,zh;q=0.9' \
  -H 'Connection: keep-alive' \
  -H 'Origin: null' \
  -H 'Sec-Fetch-Dest: empty' \
  -H 'Sec-Fetch-Mode: cors' \
  -H 'Sec-Fetch-Site: cross-site' \
  -H 'User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.0.0 Safari/537.36' \
  -H 'sec-ch-ua: "Chromium";v="130", "Google Chrome";v="130", "Not?A_Brand";v="99"' \
  -H 'sec-ch-ua-mobile: ?0' \
  -H 'sec-ch-ua-platform: "macOS"'

源是null实际也属于跨域的。

上面是的源是null 我们接下来用python模拟一个源请求

模拟跨域,就得开启一个前端服务器 然后请求后端。

2、模拟前端服务器 端口3000实现跨域

使用 Python(自带)启动 HTTP 服务器: 在终端中进入你的 index.html 所在的目录,并运行以下命令:
python -m http.server 3000

这会在 localhost:3000 上启动一个 HTTP 服务器。然后在浏览器中访问:

然后打开浏览器 访问 http://localhost:3000/index.html

打开控制台执行
fetch('http://localhost:8080/api/author/info/cross?authorName=烟火中的水滴') .then(response => response.json()) .then(data => console.log("JSON Response:", data)) .catch(error => console.error("Error:", error))

最后我们打开Network看下他发起的请求是不是真的跨域了。

通过查看curl命令确实跨域了

java 复制代码
curl 'http://localhost:8080/api/author/info/cross?authorName=%E7%83%9F%E7%81%AB%E4%B8%AD%E7%9A%84%E6%B0%B4%E6%BB%B4' \
  -H 'Accept: */*' \
  -H 'Accept-Language: zh-CN,zh;q=0.9' \
  -H 'Connection: keep-alive' \
  -H 'Origin: http://localhost:3000' \
  -H 'Referer: http://localhost:3000/' \
  -H 'Sec-Fetch-Dest: empty' \
  -H 'Sec-Fetch-Mode: cors' \
  -H 'Sec-Fetch-Site: same-site' \
  -H 'User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.0.0 Safari/537.36' \
  -H 'sec-ch-ua: "Chromium";v="130", "Google Chrome";v="130", "Not?A_Brand";v="99"' \
  -H 'sec-ch-ua-mobile: ?0' \
  -H 'sec-ch-ua-platform: "macOS"'
相关推荐
Cshaosun8 分钟前
js版本之ES5特性简述【String、Function、JSON、其他】(二)
前端·javascript·es
__WanG12 分钟前
Flutter将应用打包发布到App Store
前端·flutter·ios
leluckys14 分钟前
flutter 专题十七 Flutter Flar动画实战
前端·flutter
豆包MarsCode30 分钟前
我用豆包MarsCode IDE 做了一个 CSS 权重小组件
开发语言·前端·javascript·css·ide·html
22x艾克斯40 分钟前
Web Notifications API-让网页也能像QQ一样实现消息通知
前端
想你的风吹到了瑞士1 小时前
变量提升&函数提升
前端·javascript·vue.js
生椰拿铁You1 小时前
12 —— Webpack中向前端注入环境变量
前端
Huazzi.1 小时前
免费好用的静态网页托管平台全面对比介绍
前端·网络·github·web
吃土少女古拉拉2 小时前
前端和后端
前端·学习笔记
寒雒2 小时前
【Python】实战:实现GUI登录界面
开发语言·前端·python