登录
推荐 文章 Go 技术 课程 下载 专题 AI
首页 >  Golang >  Go教程

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 和可选端口
CrossOriginProtection 根据 Sec-Fetch-Site、Origin 和 Host 判断安全方法与非安全写请求的跨源保护边界结构图
图1:跨源保护边界说明图,展示请求元数据、策略判断和业务 Handler 的关系。

这个边界很重要:安全方法“允许通过”并不意味着可以在 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,就需要自己把错误转换成响应。

NewCrossOriginProtection、AddTrustedOrigin、SetDenyHandler 与 POST settings 业务处理器之间的静态关系结构图
图2:保护器接入结构图,展示可信 Origin、拒绝处理器和写接口 Handler 的关系。

几个容易混淆的边界

跨源保护不等于登录和授权

保护器只回答“这个请求是否符合跨源入口规则”。它不会验证 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 的职责不同,不能用其中一个代替另一个。

声明:本文转载于:17golang原创 如有侵犯,请联系study_golang@163.com删除
相关阅读
更多>
最新阅读
更多>
课程推荐
更多>