常见的跨域解决方法

1、JSONP (JSON with Padding)

由于浏览器同源策略的限制,网页中无法通过 Ajax 请求非同源的接口数据。但是script标签不受浏览器同源策略的影响,可以通过 src 属性,请求非同源的 js 脚本。
因此,JSONP 的实现原理,就是通过 script 标签的 src 属性,请求跨域的数据接口,并通过函数调用的形式,接收跨域接口响应回来的数据。
注意:是script标签中src属性,要有函数

一个简单的JSONP

先定义一个 success 回调函数,再在url中?后面调用

 <script>
   function success(data) {
     console.log('获取到了data数据:')
     console.log(data)
   }
 </script>
<script src="http://ajax.frontend.itheima.net:3006/api/jsonp?callback=success&name=zs&age=20"></script>


//服务器端响应数据必须是一个函数的调用,真正要发送给客户端的数据需要作为函数调用的参数。
 const data = 'fn({name: "张三", age: "20"})';
 res.send(data);

缺点

由于 JSONP 是通过 script 标签的 src 属性,来实现跨域数据获取的,所以,JSONP 只支持 GET 数据请求,不支持 POST 请求。

注意:JSONP 和 Ajax 之间没有任何关系,不能把 JSONP 请求数据的方式叫做 Ajax,因为 JSONP 没有用到 XMLHttpRequest 这个对象。

jQuery中的JSONP

 $.ajax({
    url: 'http://ajax.frontend.itheima.net:3006/api/jsonp?name=zs&age=20',
    // 如果要使用 $.ajax() 发起 JSONP 请求,必须指定 datatype 为 jsonp
    dataType: 'jsonp',
    success: function(res) {
       console.log(res)
    }
 })

默认情况下,使用 jQuery 发起 JSONP 请求,会自动携带一个 callback=jQueryxxx 的参数,jQueryxxx 是随机生成的一个回调函数名称。

 $.ajax({
    url: 'http://ajax.frontend.itheima.net:3006/api/jsonp?name=zs&age=20',
    dataType: 'jsonp',
    // 发送到服务端的参数名称,默认值为 callback
    jsonp: 'callback',
    // 自定义的回调函数名称,默认值为 jQueryxxx 格式
    jsonpCallback: 'abc',//参数以及回调函数名称
    success: function(res) {
       console.log(res)
    }
 })

jQuery 中的 JSONP,也是通过 <script> 标签的 src 属性实现跨域数据访问的,只不过,jQuery 采用的是动态创建和移除 <script> 标签的方式,来发起 JSONP 数据请求。
在发起 JSONP 请求的时候,动态向 <header> 中 append 一个 <script> 标签;
在 JSONP 请求成功以后,动态从 <header> 中移除刚才 append 进去的 <script> 标签;

2、CORS

CORS:全称为 Cross-origin resource sharing,即跨域资源共享,它允许浏览器向跨域服务器发送 Ajax 请求,克服了 Ajax 只能同源使用的限制。

CORS 需要浏览器后端同时支持。IE 8 和 9 需要通过 XDomainRequest 来实现。

浏览器会自动进行 CORS 通信,实现 CORS 通信的关键是后端。只要后端实现了 CORS,就实现了跨域。

服务端设置 Access-Control-Allow-Origin 就可以开启 CORS。 该属性表示哪些域名可以访问资源,如果设置通配符则表示所有网站都可以访问资源。

Node 服务器端设置响应头示例代码

 app.use((req, res, next) => {
     res.header('Access-Control-Allow-Origin', '*');//类似Access-Control-Allow-Origin: 'http://localhost:3000'

     res.header('Access-Control-Allow-Methods', 'GET, POST');
     next();
 })

withCredentials属性
在使用Ajax技术发送跨域请求时,默认情况下不会在请求中携带cookie信息。
withcredentials:指定在涉及到跨域请求时。是否携带cookie信息,默认值为false
Access-Control-Allow-Credentials: true 允许客户端发送请求时携带cookie

虽然设置 CORS 和前端没什么关系,但是通过这种方式解决跨域问题的话,会在发送请求时出现两种情况,分别为简单请求和复杂请求

简单请求

1:使用下列方法之一:GETHEADPOST
2:Content-Type 的值仅限于下列三者之一: text/plainmultipart/form-dataapplication/x-www-form-urlencoded
请求中的任意 XMLHttpRequestUpload 对象均没有注册任何事件监听器; XMLHttpRequestUpload 对象可以使用 XMLHttpRequest.upload 属性访问。

复杂请求

对于复杂请求来说,首先会发起一个预检请求,该请求是 option 方法的,通过该请求来知道服务端是否允许跨域请求。

对于预检请求来说,如果你使用过 Node 来设置 CORS 的话,可能会遇到过这么一个坑。

以下以 express 框架举例:

app.use((req, res, next) => {
  res.header('Access-Control-Allow-Origin', '*')
  res.header('Access-Control-Allow-Methods', 'PUT, GET, POST, DELETE, OPTIONS')
  res.header(
    'Access-Control-Allow-Headers',
    'Origin, X-Requested-With, Content-Type, Accept, Authorization, Access-Control-Allow-Credentials'
  )
  next()
})
//过滤 option 方法
res.statusCode = 204
res.setHeader('Content-Length', '0')
res.end()

该请求会验证你的 Authorization 字段,没有的话就会报错。

当前端发起了复杂请求后,你会发现就算你代码是正确的,返回结果也永远是报错的。因为预检请求也会进入回调中,也会触发 next 方法,因为预检请求并不包含 Authorization 字段,所以服务端会报错。

想解决这个问题很简单,只需要在回调中过滤 option 方法即可

3、document.domain

该方式只能用于二级域名相同的情况下,比如 a.test.comb.test.com 适用于该方式。

只需要给页面添加 document.domain = 'test.com' 表示二级域名都相同就可以实现跨域

4、postMessage

这种方式通常用于获取嵌入页面中的第三方页面数据。一个页面发送消息,另一个页面判断来源并接收消息

// 发送消息端
window.parent.postMessage('message', 'http://test.com')


// 接收消息端
var mc = new MessageChannel()
mc.addEventListener('message', event => {
  var origin = event.origin || event.originalEvent.origin
  if (origin === 'http://test.com') {
    console.log('验证通过')
  }
})
<think>我们正在讨论问题的解决方案。根据引用[1][2][3],问题主要由浏览器的同源策略引起,常见情况包括:名不同、端口不同、协议不同。解决方案有多种,包括CORS、JSONP、反向代理等。用户意图是寻找解决问题的方法,因此我将详细总结这些解决方案。</think>### 问题解决方案详解 问题由浏览器同源策略触发,主要解决方案如下: #### 一、CORS(资源共享)【推荐首选】[^1][^2] ```http # 服务器响应头配置示例 Access-Control-Allow-Origin: https://your-domain.com Access-Control-Allow-Methods: GET, POST, PUT Access-Control-Allow-Headers: Content-Type, Authorization Access-Control-Allow-Credentials: true # 允许携带cookie ``` **实现方式**: 1. **Spring Boot**: ```java @Configuration public class CorsConfig implements WebMvcConfigurer { @Override public void addCorsMappings(CorsRegistry registry) { registry.addMapping("/**") .allowedOrigins("https://frontend.com") .allowedMethods("*") .allowCredentials(true); } } ``` 2. **Node.js/Express**: ```javascript const express = require('express'); const cors = require('cors'); const app = express(); app.use(cors({ origin: 'https://frontend.com', methods: ['GET','POST'], credentials: true })); ``` #### 二、JSONP(仅限GET请求)[^2] **原理**:利用`<script>`标签不受同源策略限制的特性 ```html <!-- 前端实现 --> <script> function handleResponse(data) { console.log('Received:', data); } </script> <script src="https://api.com/data?callback=handleResponse"></script> ``` **后端配合**(Java示例): ```java @GetMapping("/data") public String jsonp(@RequestParam String callback) { String json = "{\"name\":\"John\", \"age\":30}"; return callback + "(" + json + ")"; // 返回 handleResponse({...}) } ``` #### 三、反向代理方案[^3] 1. **Nginx配置**: ```nginx server { listen 80; server_name frontend.com; location /api/ { proxy_pass http://backend-service:8080/; # 转发到后端 proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; } } ``` > 效果:浏览器访问`frontend.com/api/data` → Nginx转发到`backend-service:8080/data` 2. **开发环境代理**(Vue CLI): ```javascript // vue.config.js module.exports = { devServer: { proxy: { '/api': { target: 'http://localhost:3000', pathRewrite: {'^/api': ''}, // 移除/api前缀 changeOrigin: true } } } } ``` #### 四、其他解决方案 | 方案 | 适用场景 | 特点 | |--------------------|-----------------------------------|----------------------------------------------------------------------| | WebSocket | 实时通信应用 | 不受同源策略限制,但需服务端支持[^2] | | 服务器端渲染(SSR) | SEO优化/首屏加载 | 服务端直接输出HTML,避免浏览器[^2] | | API网关 | 微服务架构 | 统一处理,Spring Cloud Gateway等实现[^2] | | document.domain | 同主不同子 | 设置`document.domain = 'parent.com'`(需父子名配合) | #### 方案选择决策树 ```mermaid graph TD A[需要支持所有HTTP方法?] -->|是| B[使用CORS或反向代理] A -->|否| C{仅需GET请求?} C -->|是| D[JSONP或CORS] C -->|否| E[必须用反向代理] B --> F{控制服务器配置?} F -->|是| G[CORS] F -->|否| H[Nginx反向代理] ``` #### 最佳实践与安全建议 1. **CORS精细化控制**: ```http # 避免使用通配符* Access-Control-Allow-Origin: https://trusted-domain.com ``` 2. **预检请求处理**: ```java // Spring Boot OPTIONS处理 @RequestMapping(value = "/**", method = RequestMethod.OPTIONS) public ResponseEntity<?> handleOptions() { return ResponseEntity.ok() .header("Access-Control-Allow-Methods", "GET,POST") .build(); } ``` 3. **Cookie安全**: - 前端设置`withCredentials: true` - 服务端需返回`Access-Control-Allow-Credentials: true` - 避免使用`Access-Control-Allow-Origin: *`[^2] > 关键原则:**优先使用CORS标准方案**,反向代理适用于无法修改服务端配置的场景[^1][^2][^3]。 ---
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

waves0001

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值