跨域

2024-07-17

跨域的介绍

跨域请求(Cross-Origin Request)是指从一个域(Origin)向另一个域发起的HTTP请求。通常情况下,浏览器出于安全考虑,会阻止跨域请求,以防止潜在的跨站点脚本攻击(XSS)。这种安全策略称为同源策略(Same-Origin Policy)。

什么是同源策略?

同源策略要求以下三个组成部分完全相同,才允许相互访问资源:

  1. 协议(如http, https)
  2. 域名(如example.com)
  3. 端口号(如80, 443)

例如:

  • http://example.comhttp://example.com 是同源的。
  • http://example.comhttps://example.com 不是同源的(协议不同)。
  • http://example.comhttp://sub.example.com 不是同源的(域名不同)。
  • http://example.com:80http://example.com:8080 不是同源的(端口不同)。

跨域请求的场景

跨域请求通常在以下情况下出现:

  • 前端代码运行在http://frontend.com,需要访问http://api.backend.com的资源。
  • 使用CDN资源(如字体、图片)时,CDN的域名与主站点的域名不同。
  • 使用第三方服务的API(如支付网关、地图服务)。

如何解决跨域请求问题?

为了允许合法的跨域请求,Web应用可以使用CORS(跨域资源共享,Cross-Origin Resource Sharing)来实现。CORS是一种机制,它通过设置HTTP头来告诉浏览器允许跨域资源请求。以下是一些常用的方法来实现CORS:

1. 设置CORS头

服务器可以通过在响应中设置Access-Control-Allow-Origin头来允许跨域请求。例如:

1
Access-Control-Allow-Origin: *

或者指定特定的域:

1
Access-Control-Allow-Origin: http://allowed-origin.com

2. 预检请求(Preflight Request)

对于某些复杂请求(如使用PUTDELETE方法,或发送自定义头),浏览器会首先发送一个OPTIONS请求,这称为预检请求。服务器需要对预检请求进行响应,并设置相应的CORS头:

1
2
3
Access-Control-Allow-Methods: POST, GET, OPTIONS, PUT, DELETE
Access-Control-Allow-Headers: Content-Type, Authorization
Access-Control-Allow-Origin: http://allowed-origin.com

3. 使用代理服务器

如果不能修改目标服务器,可以通过设置代理服务器来解决跨域问题。前端请求发送到代理服务器,由代理服务器再转发请求到目标服务器,这样对于浏览器来说,所有请求都是同源的。

4. JSONP(JSON with Padding)

在CORS普及之前,JSONP是一种常见的跨域解决方案。它通过动态创建<script>标签,利用脚本标签不受同源策略限制的特性来实现跨域请求。不过,JSONP只支持GET请求,而且存在安全隐患,现在已经较少使用。

go处理跨域请求

示例代码

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
package main

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

func main() {
    r := gin.Default()

    // 中间件处理跨域请求
    r.Use(func(c *gin.Context) {
        c.Writer.Header().Set("Access-Control-Allow-Origin", "*")
        c.Writer.Header().Set("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS")
        c.Writer.Header().Set("Access-Control-Allow-Headers", "Content-Type, Authorization")

        if c.Request.Method == "OPTIONS" {
            c.AbortWithStatus(http.StatusNoContent)
            return
        }

        c.Next()
    })

    // 示例API路由
    r.GET("/data", func(c *gin.Context) {
        c.JSON(http.StatusOK, gin.H{
            "message": "This is a cross-origin response!",
        })
    })

    r.Run(":8080") // 启动服务器,监听8080端口
}

测试跨域请求

你可以使用以下HTML代码在浏览器中测试跨域请求:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>CORS Test</title>
</head>
<body>
    <button id="fetchButton">Fetch Data</button>
    <pre id="result"></pre>

    <script>
        document.getElementById('fetchButton').addEventListener('click', () => {
            fetch('http://localhost:8080/data')
                .then(response => response.json())
                .then(data => {
                    document.getElementById('result').textContent = JSON.stringify(data, null, 2);
                })
                .catch(error => {
                    console.error('Error:', error);
                });
        });
    </script>
</body>
</html>

打开该HTML文件,在浏览器中点击“Fetch Data”按钮,如果服务器正确处理了跨域请求,你将看到响应数据。

跨域请求的检测

跨域请求(Cross-Origin Request)的检测和限制主要由浏览器来执行,而不是由服务器来执行。这是因为浏览器实施了同源策略(Same-Origin Policy),它是一个重要的安全机制,旨在防止恶意网站通过脚本访问用户的敏感信息或执行恶意操作。

同源策略(Same-Origin Policy)

同源策略要求在以下三个方面完全匹配才认为两个请求是同源的:

  1. 协议(Protocol):例如httphttps
  2. 域名(Domain):例如example.com
  3. 端口(Port):例如80443

如果两个请求在上述任何一个方面不匹配(即一个或多个部分不同),浏览器就会认为这是跨域请求,然后根据同源策略进行相应的限制。

跨域请求的检测

当浏览器发现一个请求跨越了同源限制时,它会采取以下措施:

  1. 阻止请求发送:浏览器会阻止跨域请求的发送,不会向目标服务器发送实际的HTTP请求。

  2. 不传递响应数据:即使服务器返回了响应,浏览器也会阻止页面脚本访问响应数据,从而保护用户数据安全。

  3. 预检请求(Preflight Request):对于某些复杂的跨域请求(如使用PUTDELETE等方法或发送自定义头信息),浏览器会首先发送一个预检请求(OPTIONS请求),来询问服务器是否允许实际的请求。只有服务器返回了合适的CORS响应头,浏览器才会发送实际的请求。

服务器的角色

虽然服务器不会直接执行跨域请求的检测和限制,但它可以通过设置响应头来告知浏览器如何处理跨域请求。服务器可以:

  • 设置Access-Control-Allow-Origin头来允许特定或所有来源的跨域请求。
  • 设置Access-Control-Allow-Methods头来允许的HTTP方法。
  • 设置Access-Control-Allow-Headers头来允许的自定义请求头。
  • 处理预检请求(OPTIONS请求)并正确响应。

通过正确设置这些响应头,服务器可以与浏览器协作,使得合法的跨域请求能够顺利完成,从而实现跨域资源共享(CORS)。

浏览器检测服务端是否支持跨域的步骤

当浏览器发送一个跨域请求时,它会进行一系列步骤来检测服务器是否支持跨域资源共享(CORS)。这些步骤主要涉及发送预检请求(OPTIONS请求)和检查服务器返回的响应头。以下是浏览器检测服务端是否支持跨域的详细步骤:

1. 发送跨域请求

浏览器中的JavaScript代码发起一个跨域请求,例如使用fetchXMLHttpRequest等方法。

2. 发送预检请求(OPTIONS请求)

如果满足以下条件之一,浏览器会首先发送一个预检请求(OPTIONS请求)到目标服务器:

  • 使用了非简单请求(例如使用了自定义头部信息)。
  • 使用了不常见的HTTP方法(例如PUT、DELETE等)。

3. 预检请求的内容

预检请求中的主要内容包括:

  • 请求方法(Method):通常为OPTIONS。
  • 请求头部(Headers):包括发起请求时设置的自定义头部信息。
  • 来源(Origin):指示请求的来源地址。

4. 服务器处理预检请求

服务器接收到预检请求后,需要进行以下处理:

  • 验证请求来源(Origin):检查请求是否来自合法的来源。可以通过比对请求中的Origin字段与服务器允许的来源进行匹配来确认。

  • 验证请求方法和头部信息:检查请求是否使用了允许的HTTP方法(如GET、POST、PUT、DELETE等)和头部信息。

  • 设置CORS响应头:如果服务器支持跨域请求,需要设置适当的CORS响应头。主要包括:

    • Access-Control-Allow-Origin:指定允许访问的来源。可以设置为具体的来源或*表示允许所有来源。
    • Access-Control-Allow-Methods:指定允许的HTTP方法。
    • Access-Control-Allow-Headers:指定允许的自定义头部信息。
    • Access-Control-Allow-Credentials:是否允许发送Cookie信息(如果请求中包含)。

5. 浏览器处理预检请求响应

浏览器收到服务器的预检请求响应后,会进行以下处理:

  • 检查Access-Control-Allow-Origin:如果服务器返回了合法的Access-Control-Allow-Origin头(包括*通配符),浏览器认为跨域请求受到允许,继续发送实际请求。

  • 检查其他CORS头部:浏览器还会检查Access-Control-Allow-MethodsAccess-Control-Allow-Headers等头部信息,确保服务器允许使用的HTTP方法和头部信息。

6. 发送实际请求

如果预检请求的响应符合CORS规范要求,浏览器会继续发送实际的跨域请求(如GET、POST等),并将用户的Cookie等信息包含在请求中。

7. 处理实际请求响应

最终,服务器接收并处理实际请求,并返回响应。浏览器在收到响应后,会根据同源策略(Same-Origin Policy)来决定是否允许页面脚本访问响应数据。

总结

浏览器通过发送预检请求(OPTIONS请求)和检查服务器返回的CORS响应头来检测服务器是否支持跨域。服务器需要正确设置响应头,以允许合法的跨域请求。这种机制帮助确保跨域请求的安全性和合法性,防止恶意网站利用脚本攻击用户信息。

0%