Go http.CrossOriginProtection 怎样给写接口加跨源请求保护
来源:17golang原创
时间:2026-10-21 22:20:26 464浏览 收藏
如果一个 Go 服务有修改配置、提交表单或删除资源的写接口,想先挡住浏览器发起的非安全跨源请求,可以在 net/http 的 Handler 层接入 http.CrossOriginProtection。它会根据 Sec-Fetch-Site,以及 Origin 与 Host 的关系判断请求是否跨源;被判定为不允许的请求会在进入业务处理器前得到拒绝。
本文使用的官方地址:https://pkg.go.dev/net/http#CrossOriginProtection
这是一层跨源请求保护,不是登录系统,也不是授权系统。读者真正要做的是三件事:明确哪些写接口需要保护,给确实存在的前端来源配置可信 Origin,再把拒绝响应做成统一且不泄露内部细节的 403。
先区分保护范围
CrossOriginProtection 的核心价值是把“浏览器跨源请求是否可以触达 Handler”变成一个标准库层面的判断。它不需要你在每个 POST 处理函数里重复读取请求头,也不应该被误解成所有 HTTP 请求的身份验证器。
| 请求或配置 | 处理方式 | 工程含义 |
|---|---|---|
GET、HEAD、OPTIONS | 安全方法,保护器始终允许 | 不要让安全方法承担修改状态的副作用 |
带跨源信号的 POST、PUT、PATCH、DELETE | 进入跨源检查 | 不符合规则时在业务 Handler 前拒绝 |
没有 Sec-Fetch-Site 或 Origin 的请求 | 当前实现通常按同源或非浏览器请求处理 | 不能把它当作“已完成身份认证” |
| 明确加入的可信 Origin | 允许该 Origin 的请求继续通过 | 必须写完整的 scheme、host 和可选端口 |

这个边界很重要:安全方法“允许通过”并不意味着可以在 GET 中修改数据库;没有来源头的请求“允许继续”也不意味着它已经通过了用户登录和权限检查。跨源保护解决的是浏览器携带站点身份信息发起写操作时的一类请求入口问题。
把保护器接到 ServeMux
最小接入方式是创建保护器,用它的 Handler 包住已有的 ServeMux。保护器位于路由处理器之外,写接口仍然由原来的业务函数负责。
package main
import (
"encoding/json"
"log"
"net/http"
)
func main() {
mux := http.NewServeMux()
mux.HandleFunc("POST /settings", updateSettings)
// 保护器统一包住路由,避免每个写接口各写一套来源判断。
protection := http.NewCrossOriginProtection()
handler := protection.Handler(mux)
// 监听入口只接收包装后的 Handler,业务路由保持原样。
log.Fatal(http.ListenAndServe(":8080", handler))
}
func updateSettings(w http.ResponseWriter, r *http.Request) {
// 这里仍然要做登录、授权、参数校验和幂等控制。
w.WriteHeader(http.StatusNoContent)
}
var _ = json.NewEncoder
示例中的保护器只负责跨源入口,updateSettings 仍要验证当前用户是否登录、是否有修改配置的权限,以及请求数据是否符合业务规则。把这些职责混到 CrossOriginProtection 中,会让后续排查变得困难。
配置可信 Origin
如果管理前端和 Go 服务不是同一个 Origin,可以调用 AddTrustedOrigin。参数是完整的 Origin,例如 https://admin.example.test,不是只写域名,也不是把路径拼进去。该方法返回错误,因此启动阶段应直接处理配置错误。
func buildHandler(mux http.Handler) (http.Handler, error) {
protection := http.NewCrossOriginProtection()
// Origin 要包含协议、主机和需要时使用的端口,避免模糊匹配。
if err := protection.AddTrustedOrigin("https://admin.example.test"); err != nil {
return nil, fmt.Errorf("添加可信 origin 失败: %w", err)
}
// 保护器放在路由外层,统一检查跨源浏览器请求。
return protection.Handler(mux), nil
}
上面的片段需要在完整文件中引入 fmt。这里故意让配置错误向启动阶段返回,而不是忽略错误继续提供一个来源规则不完整的服务。开发、测试和生产环境的前端 Origin 也应分别配置,不要为了省事把任意来源加入信任列表。
用 SetDenyHandler 统一返回 403
默认拒绝结果是 403。如果 API 有统一的 JSON 错误格式,可以用 SetDenyHandler 替换默认处理器。拒绝处理器不要输出请求头原文、内部路由或调试堆栈,只返回足够让前端识别的稳定字段。
func configureDenyResponse(protection *http.CrossOriginProtection) {
protection.SetDenyHandler(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
// 拒绝原因只返回稳定错误码,不回显 Origin 等请求细节。
w.Header().Set("Content-Type", "application/json; charset=utf-8")
w.WriteHeader(http.StatusForbidden)
_ = json.NewEncoder(w).Encode(map[string]string{
"code": "cross_origin_request_denied",
"message": "跨源请求被拒绝",
})
}))
}
SetDenyHandler 只影响 Handler 发现应该拒绝的请求;直接调用 Check 时不会调用这个拒绝处理器。如果你在中间件链中手动调用 Check,就需要自己把错误转换成响应。

几个容易混淆的边界
跨源保护不等于登录和授权
保护器只回答“这个请求是否符合跨源入口规则”。它不会验证 Session、JWT、API Key,也不会判断用户能否修改某一条配置。通过保护器之后,仍要执行认证、授权、输入校验和业务审计。
它不负责开放跨域读取
CORS 主要描述浏览器能否读取跨源响应,CrossOriginProtection 主要拒绝不安全的跨源浏览器请求。二者解决的问题不同;如果前端确实需要跨源读取响应,还应单独设计 CORS 响应头和允许范围。
不要把绕过模式当成默认配置
AddInsecureBypassPattern 的语义是让匹配路径绕过保护,名称本身已经说明了风险。只有在你能明确证明该路径不会改变状态、并且已经有其他入口控制时才考虑使用;不要用它来“修复”一个不清楚来源的 403。
上线前的判断清单
- 写接口是否真的使用了
POST、PUT、PATCH或DELETE,且没有把修改动作藏在 GET 中? - 可信 Origin 是否包含正确的协议、主机和端口,并按环境分别配置?
- 保护器是否包住了所有需要保护的路由,而不是只包住某一个 Handler?
- 拒绝响应是否固定为 403 和稳定错误码,没有回显 Origin、Cookie 或内部堆栈?
- 认证、授权、参数校验、幂等和审计是否仍然保留?
- 是否把 CORS 需求和跨源写请求保护拆开设计?
如果你的目标是给 Go 的配置写接口加一层标准化的跨源请求保护,推荐把 CrossOriginProtection 放在路由外层,再用 AddTrustedOrigin 描述真实前端来源,用 SetDenyHandler 统一错误格式。它的适用力在于收紧请求入口,而不是替代整套 Web 安全控制。
相关问题
为什么 POST 请求没有 Origin 也可能继续执行?
官方实现会把缺少 Sec-Fetch-Site 或 Origin 的请求暂时视为同源或非浏览器请求。它不是通用的请求身份判断器,服务端仍要做好认证和授权。
可信 Origin 可以只写域名吗?
不建议。AddTrustedOrigin 接收的是完整 Origin,通常应写成包含协议、主机和端口的形式,避免把不同协议或端口混为一谈。
用了 CrossOriginProtection 还需要 CORS 吗?
如果前端需要读取跨源响应,仍可能需要 CORS;跨源保护和 CORS 的职责不同,不能用其中一个代替另一个。
-
860 收藏
-
843 收藏
-
826 收藏
-
809 收藏
-
792 收藏
-
155 收藏
-
123 收藏
-
117 收藏
-
203 收藏
-
456 收藏
-
276 收藏
-
388 收藏
-
233 收藏
-
269 收藏
-
313 收藏
-
275 收藏
-
294 收藏
-
- 前端进阶之JavaScript设计模式
- 设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
- 立即学习 543次学习
-
- GO语言核心编程课程
- 本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
- 立即学习 516次学习
-
- 简单聊聊mysql8与网络通信
- 如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
- 立即学习 500次学习
-
- JavaScript正则表达式基础与实战
- 在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
- 立即学习 487次学习
-
- 从零制作响应式网站—Grid布局
- 本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
- 立即学习 485次学习