跨域问题的本质跨域问题主要来源于浏览器的安全策略——同源策略(Same-originpolicy)。这个策略限制了来自不同源的“写”操作(如XMLHttpRequest请求)。当一个网页尝试从不同于当前文档域名的另一个域名获取资源时,就会遇到跨域问题。CORS简介CORS(Cross-Ori
跨域问题主要来源于浏览器的安全策略——同源策略(Same-origin policy)。这个策略限制了来自不同源的“写”操作(如XMLHttpRequest请求)。当一个网页尝试从不同于当前文档域名的另一个域名获取资源时,就会遇到跨域问题。
CORS(Cross-Origin Resource Sharing,跨源资源共享)是一种机制,它使用额外的HTTP头部让浏览器与服务器进行沟通,从而决定一个网页是否可以被允许访问另一个来源的资源。
Go语言本身并没有内置对CORS的支持,但可以通过自定义中间件来轻松实现。下面我们将详细介绍如何编写这样一个中间件。
基本的HTTP服务设置 首先,我们需要创建一个基本的HTTP服务作为基础。
package main
import (
"fmt"
"net/http"
)
func main() {
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "Hello, World!")
})
http.ListenAndServe(":8080", nil)
}
添加CORS支持 接下来,我们添加一个简单的CORS中间件。
func corsMiddleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Access-Control-Allow-Origin", "*") // 允许任何源访问
w.Header().Set("Access-Control-Allow-Methods", "POST, GET, OPTIONS, PUT, DELETE")
w.Header().Set("Access-Control-Allow-Headers", "Accept, Content-Type, Content-Length, Accept-Encoding, X-CSRF-Token, Authorization")
if r.Method == "OPTIONS" {
return
}
next.ServeHTTP(w, r)
})
}
然后修改主函数以使用这个中间件:
func main() {
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "Hello, World!")
})
handler := corsMiddleware(http.DefaultServeMux)
http.Handle("/", handler)
http.ListenAndServe(":8080", nil)
}
测试CORS功能 为了验证我们的CORS配置是否有效,可以使用Postman或者编写一个简单的前端页面来进行测试。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>CORS Test</title>
</head>
<body>
<button id="fetchData">Fetch Data</button>
<script>
document.getElementById('fetchData').addEventListener('click', function() {
fetch('http://localhost:8080', { method: 'GET' })
.then(response => response.text())
.then(data => console.log(data))
.catch(error => console.error('Error:', error));
});
</script>
</body>
</html>
在实际应用中,CORS配置可能需要更加细致和灵活。以下是一些进阶配置的例子。
限制特定域名访问 如果希望限制CORS仅对某些特定域名开放,可以在中间件中加入相应的逻辑。
func corsMiddleware(allowedOrigins []string) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
var origin string
for _, o := range allowedOrigins {
if r.Header.Get("Origin") == o {
origin = o
break
}
}
if origin != "" {
w.Header().Set("Access-Control-Allow-Origin", origin)
w.Header().Set("Access-Control-Allow-Methods", "POST, GET, OPTIONS, PUT, DELETE")
w.Header().Set("Access-Control-Allow-Headers", "Accept, Content-Type, Content-Length, Accept-Encoding, X-CSRF-Token, Authorization")
if r.Method == "OPTIONS" {
return
}
} else {
http.Error(w, "Forbidden", http.StatusForbidden)
return
}
http.DefaultServeMux.ServeHTTP(w, r)
})
}
func main() {
allowedOrigins := []string{"https://example.com", "http://localhost:3000"}
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "Hello, World!")
})
handler := corsMiddleware(allowedOrigins)
http.Handle("/", handler)
http.ListenAndServe(":8080", nil)
}
动态配置CORS 在一些场景下,CORS配置可能需要根据请求动态调整。可以通过环境变量或配置文件来实现这一点。
import (
"os"
)
func corsMiddleware() http.Handler {
allowedOrigins := os.Getenv("ALLOWED_ORIGINS")
if allowedOrigins == "" {
allowedOrigins = "*"
}
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Access-Control-Allow-Origin", allowedOrigins)
w.Header().Set("Access-Control-Allow-Methods", "POST, GET, OPTIONS, PUT, DELETE")
w.Header().Set("Access-Control-Allow-Headers", "Accept, Content-Type, Content-Length, Accept-Encoding, X-CSRF-Token, Authorization")
if r.Method == "OPTIONS" {
return
}
http.DefaultServeMux.ServeHTTP(w, r)
})
}
func main() {
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "Hello, World!")
})
handler := corsMiddleware()
http.Handle("/", handler)
http.ListenAndServe(":8080", nil)
}
虽然手动实现CORS中间件可以更好地控制细节,但在实际项目中,使用第三方库可以大大简化开发过程。例如,github.com/gorilla/handlers
提供了一个非常方便的CORS中间件。
安装第三方库
首先安装 github.com/gorilla/handlers
库:
go get github.com/gorilla/handlers
使用 gorilla/handlers 实现CORS
package main
import (
"fmt"
"net/http"
"github.com/gorilla/handlers"
)
func main() {
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "Hello, World!")
})
corsHandler := handlers.CORS(
handlers.AllowedOrigins([]string{"*"}),
handlers.AllowedMethods([]string{"GET", "POST", "PUT", "DELETE", "OPTIONS"}),
handlers.AllowedHeaders([]string{"Accept", "Content-Type", "Content-Length", "Accept-Encoding", "X-CSRF-Token", "Authorization"}),
)(http.DefaultServeMux)
http.Handle("/", corsHandler)
http.ListenAndServe(":8080", nil)
}
虽然CORS提供了灵活性,但也带来了一定的安全风险。以下是一些需要注意的事项:
假设有一个真实的前后端分离项目,前端运行在 http://localhost:3000,后端运行在 http://localhost:8080。我们需要实现跨域请求。
后端代码
package main
import (
"fmt"
"net/http"
"github.com/gorilla/handlers"
)
func main() {
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "Hello, World!")
})
corsHandler := handlers.CORS(
handlers.AllowedOrigins([]string{"http://localhost:3000"}),
handlers.AllowedMethods([]string{"GET", "POST", "PUT", "DELETE", "OPTIONS"}),
handlers.AllowedHeaders([]string{"Accept", "Content-Type", "Content-Length", "Accept-Encoding", "X-CSRF-Token", "Authorization"}),
)(http.DefaultServeMux)
http.Handle("/", corsHandler)
http.ListenAndServe(":8080", nil)
}
前端代码
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>CORS Test</title>
</head>
<body>
<button id="fetchData">Fetch Data</button>
<script>
document.getElementById('fetchData').addEventListener('click', function() {
fetch('http://localhost:8080', { method: 'GET' })
.then(response => response.text())
.then(data => console.log(data))
.catch(error => console.error('Error:', error));
});
</script>
</body>
</html>
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!