跨域资源共享是浏览器为了安全限制的一种机制,当前端请求的协议、域名、端口和后端服务不一致时,浏览器会拦截请求,除非后端服务返回正确的跨域响应头。在Golang中我们可以通过多种方式实现允许指定域名访问的跨域策略。

跨域资源共享核心响应头说明
要实现跨域控制,后端需要返回以下几个关键响应头,我们可以根据实际需求调整这些头的内容:
- Access-Control-Allow-Origin:指定允许访问的域名,设置为具体域名或者*(允许所有域名,不建议生产环境使用)
- Access-Control-Allow-Methods:指定允许的HTTP请求方法,比如GET,POST,PUT,DELETE等
- Access-Control-Allow-Headers:指定允许的请求头,比如Content-Type,Authorization等
- Access-Control-Allow-Credentials:是否允许携带Cookie等凭证信息,设置为true时Allow-Origin不能为*
- Access-Control-Max-Age:预检请求的缓存时间,单位是秒,减少重复预检请求
手动编写Golang跨域中间件
我们可以自己编写一个HTTP中间件,在请求处理前添加跨域响应头,实现允许指定域名访问的逻辑。首先定义一个允许访问的域名白名单,然后校验请求的Origin是否在白名单中。
package main
import (
"net/http"
"strings"
)
// 允许访问的域名白名单,实际使用时可以替换为自己的业务域名
var allowedOrigins = []string{
"https://www.ipipp.com",
"http://localhost:3000",
}
// 校验请求的来源域名是否在白名单中
func isOriginAllowed(origin string) bool {
for _, allowed := range allowedOrigins {
if strings.EqualFold(origin, allowed) {
return true
}
}
return false
}
// 跨域中间件
func corsMiddleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
origin := r.Header.Get("Origin")
// 如果请求有Origin头且来源在白名单中,设置允许跨域的响应头
if origin != "" && isOriginAllowed(origin) {
w.Header().Set("Access-Control-Allow-Origin", origin)
w.Header().Set("Access-Control-Allow-Methods", "GET,POST,PUT,DELETE,OPTIONS")
w.Header().Set("Access-Control-Allow-Headers", "Content-Type,Authorization")
w.Header().Set("Access-Control-Allow-Credentials", "true")
w.Header().Set("Access-Control-Max-Age", "86400")
}
// 处理预检请求,直接返回200状态码
if r.Method == "OPTIONS" {
w.WriteHeader(http.StatusOK)
return
}
next.ServeHTTP(w, r)
})
}
// 示例业务处理函数
func helloHandler(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("Hello, CORS enabled Golang service"))
}
func main() {
mux := http.NewServeMux()
mux.HandleFunc("/hello", helloHandler)
// 使用跨域中间件包装路由
handler := corsMiddleware(mux)
http.ListenAndServe(":8080", handler)
}
使用第三方库实现跨域配置
如果不想手动处理跨域逻辑,也可以使用成熟的第三方库,比如github.com/rs/cors,这个库已经封装了完整的跨域处理逻辑,配置起来更加简单。
首先安装依赖:
go get github.com/rs/cors
然后使用库提供的配置项设置允许指定域名访问:
package main
import (
"net/http"
"github.com/rs/cors"
)
func helloHandler(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("Hello, CORS enabled by third party library"))
}
func main() {
mux := http.NewServeMux()
mux.HandleFunc("/hello", helloHandler)
// 配置跨域规则,允许指定域名访问
c := cors.New(cors.Options{
AllowedOrigins: []string{"https://www.ipipp.com", "http://localhost:3000"},
AllowedMethods: []string{"GET", "POST", "PUT", "DELETE", "OPTIONS"},
AllowedHeaders: []string{"Content-Type", "Authorization"},
AllowCredentials: true,
MaxAge: 86400,
})
// 包装路由处理器
handler := c.Handler(mux)
http.ListenAndServe(":8080", handler)
}
常见问题排查
配置完成后如果跨域仍然不生效,可以排查以下几个点:
- 检查请求的Origin是否在允许的白名单中,注意域名要完全匹配,包括协议和端口
- 如果设置了AllowCredentials为true,AllowedOrigins不能包含*,必须指定具体域名
- 预检请求的响应状态码是否正确,手动编写中间件时要确保OPTIONS请求返回200
- 响应头是否被其他中间件覆盖,确保跨域中间件的执行顺序在其他业务中间件之前
注意事项
生产环境中不要直接使用*允许所有域名访问,这样会带来跨站请求伪造的安全风险,一定要根据业务需求配置具体的允许域名。如果业务需要支持多个动态域名,可以将白名单存储在配置文件或者数据库中,定期更新,避免硬编码导致维护成本过高。