首页 > 后端开发 > Golang > 正文

Go语言WebSocket服务:解决403错误与Origin校验

花韻仙語
发布: 2025-11-06 23:04:01
原创
326人浏览过

go语言websocket服务:解决403错误与origin校验

本文旨在解决Go语言使用`go.net/websocket`包时常见的403 Forbidden错误,该错误通常源于默认的`Origin`头部校验机制。我们将深入探讨`websocket.Handler`与`websocket.Server`的区别,并提供使用`websocket.Server`来绕过或自定义`Origin`校验的解决方案,确保WebSocket连接的顺利建立,同时强调相关安全考量。

1. Go语言WebSocket基础服务构建与常见问题

在Go语言中,使用go.net/websocket包可以方便地构建WebSocket服务。一个常见的WebSocket Echo服务器示例如下,它旨在接收客户端消息并打印:

package main

import (
    "fmt"
    "golang.org/x/net/websocket" // 注意:原先的code.google.com/p/go.net/websocket已迁移至此
    "net/http"
)

// webHandler 处理WebSocket连接,接收消息并打印
func webHandler(ws *websocket.Conn) {
    var s string
    // 从WebSocket连接中读取字符串
    if _, err := fmt.Fscan(ws, &s); err != nil {
        fmt.Printf("Error reading from websocket: %v\n", err)
        return
    }
    fmt.Println("Received: ", s)
    // 可以选择将消息回显给客户端
    // if _, err := fmt.Fprint(ws, "Echo: "+s); err != nil {
    //  fmt.Printf("Error writing to websocket: %v\n", err)
    // }
}

func main() {
    fmt.Println("Starting websocket server on :8080/echo")
    // 将webHandler包装成websocket.Handler并注册到HTTP路由
    http.Handle("/echo", websocket.Handler(webHandler))

    // 启动HTTP服务器监听8080端口
    err := http.ListenAndServe(":8080", nil)
    if err != nil {
        panic("ListenAndServe: " + err.Error())
    }
}
登录后复制

对应的客户端JavaScript连接代码如下:

// 尝试连接到WebSocket服务
ws = new WebSocket("ws://localhost:8080/echo");

// 监听接收到的消息
ws.onmessage = function(e) {
    console.log("websock: " + e.data);
};

// 连接成功时发送消息
ws.onopen = function() {
    ws.send("Hello from client!");
};

// 连接关闭时
ws.onclose = function() {
    console.log("WebSocket connection closed.");
};

// 发生错误时
ws.onerror = function(err) {
    console.error("WebSocket error:", err);
};
登录后复制

然而,当尝试使用上述客户端代码连接到服务器时,浏览器控制台可能会报告WebSocket connection to 'ws://localhost:8080/echo' failed: Unexpected response code: 403错误。这表明服务器拒绝了连接请求。

立即学习go语言免费学习笔记(深入)”;

2. 深入理解403错误:Origin校验机制

这个403错误通常与go.net/websocket包中websocket.Handler的默认行为有关。websocket.Handler在处理WebSocket握手时,会默认检查HTTP请求头中的Origin字段。Origin头是浏览器在发起跨域请求(包括WebSocket连接)时自动添加的,用于指示请求的来源域。

websocket.Handler的默认行为是:

  • 它期望Origin头部是一个有效的URL。
  • 它会尝试验证Origin是否与服务器的地址匹配,或者至少是一个可接受的来源。
  • 如果Origin头部缺失、无效,或者不符合其内部的默认校验规则,websocket.Handler就会拒绝连接,并返回403 Forbidden状态码。

这种机制主要是为了增强基于浏览器的WebSocket连接的安全性,防止跨站请求伪造(CSRF)等攻击。然而,在某些场景下,例如:

挖错网
挖错网

一款支持文本、图片、视频纠错和AIGC检测的内容审核校对平台。

挖错网 28
查看详情 挖错网
  • 非浏览器客户端(如服务器到服务器的WebSocket通信)可能不发送Origin头部。
  • Origin头部与服务器地址不完全匹配,但连接是合法的。
  • 开发或测试环境中,需要临时禁用Origin校验。

在这些情况下,websocket.Handler的默认Origin校验就会成为障碍。

3. 解决方案:使用websocket.Server绕过或自定义Origin校验

为了解决Origin校验导致的403错误,我们可以使用websocket.Server结构体来替代直接使用websocket.Handler。websocket.Server提供了更灵活的配置选项,尤其是在处理握手阶段。

websocket.Server允许我们直接嵌入一个websocket.Handler,但更重要的是,它提供了一个Handshake字段,可以自定义握手逻辑。当Handshake字段为nil时,websocket.Server会采用一个默认的握手函数,这个默认函数不会强制执行Origin校验

下面是修改后的Go服务器代码,它使用websocket.Server来解决403问题:

package main

import (
    "fmt"
    "golang.org/x/net/websocket" // 注意:请确保导入路径正确
    "net/http"
)

// webHandler 处理WebSocket连接,接收消息并打印
func webHandler(ws *websocket.Conn) {
    var s string
    // 从WebSocket连接中读取字符串
    if _, err := fmt.Fscan(ws, &s); err != nil {
        // 忽略EOF错误,这通常表示客户端关闭了连接
        if err.Error() != "EOF" {
            fmt.Printf("Error reading from websocket: %v\n", err)
        }
        return
    }
    fmt.Println("Received: ", s)
    // 可以选择将消息回显给客户端
    if _, err := fmt.Fprint(ws, "Echo: "+s); err != nil {
        fmt.Printf("Error writing to websocket: %v\n", err)
    }
}

func main() {
    fmt.Println("Starting websocket server on :8080/echo")

    // 使用http.HandleFunc注册一个自定义处理函数
    http.HandleFunc("/echo",
        func(w http.ResponseWriter, req *http.Request) {
            // 创建websocket.Server实例
            // Handshake字段为nil时,默认的握手函数不会强制检查Origin
            s := websocket.Server{Handler: websocket.Handler(webHandler)}
            // 调用ServeHTTP方法来处理WebSocket连接
            s.ServeHTTP(w, req)
        })

    // 启动HTTP服务器监听8080端口
    err := http.ListenAndServe(":8080", nil)
    if err != nil {
        panic("ListenAndServe: " + err.Error())
    }
}
登录后复制

在这个修正后的代码中,我们不再直接将websocket.Handler(webHandler)传递给http.Handle。取而代之的是,我们创建了一个websocket.Server实例,并将websocket.Handler(webHandler)赋值给它的Handler字段。然后,通过s.ServeHTTP(w, req)方法来处理HTTP请求。由于websocket.Server的Handshake字段默认为nil,它将使用一个不执行Origin校验的默认握手函数,从而允许连接成功建立。

4. 示例代码与注意事项

完整的服务器代码

package main

import (
    "fmt"
    "golang.org/x/net/websocket"
    "net/http"
    "log" // 引入log包用于更专业的错误处理
)

// webHandler 处理WebSocket连接,接收消息并打印,并回显
func webHandler(ws *websocket.Conn) {
    defer ws.Close() // 确保连接关闭

    var msg string
    for {
        // 从WebSocket连接中读取字符串
        err := websocket.Message.Receive(ws, &msg) // 使用websocket.Message进行结构化读写
        if err != nil {
            if err.Error() != "EOF" { // 忽略EOF错误,通常表示客户端关闭了连接
                log.Printf("Error receiving message from websocket: %v\n", err)
            }
            break // 发生错误或客户端关闭时退出循环
        }
        log.Printf("Received from client %s: %s\n", ws.RemoteAddr().String(), msg)

        // 将消息回显给客户端
        err = websocket.Message.Send(ws, "Echo: "+msg)
        if err != nil {
            log.Printf("Error sending message to websocket: %v\n", err)
            break
        }
    }
    log.Printf("Client %s disconnected.\n", ws.RemoteAddr().String())
}

func main() {
    log.Println("Starting websocket server on :8080/echo")

    http.HandleFunc("/echo",
        func(w http.ResponseWriter, req *http.Request) {
            // 可以选择在此处手动检查Origin头部,以增加安全性
            // if req.Header.Get("Origin") != "http://localhost:8080" {
            //  http.Error(w, "Forbidden", http.StatusForbidden)
            //  return
            // }

            // 创建websocket.Server实例,Handler字段指向我们的业务逻辑
            // Handshake字段为nil时,默认的握手函数不会强制检查Origin
            s := websocket.Server{Handler: websocket.Handler(webHandler)}

            // 调用ServeHTTP方法来处理WebSocket连接
            s.ServeHTTP(w, req)
        })

    log.Fatal(http.ListenAndServe(":8080", nil)) // 使用log.Fatal来处理ListenAndServe错误
}
登录后复制

注意事项

  1. 安全性考虑: 禁用Origin校验会降低WebSocket连接的安全性,使其更容易受到CSRF攻击。在生产环境中,特别是面向浏览器的应用,强烈建议:
    • 启用Origin校验: 仅允许来自受信任域名的连接。如果使用websocket.Server,可以在Handshake字段中提供一个自定义函数来执行精确的Origin验证。
    • 使用TLS/SSL (HTTPS/WSS): 保护通信内容不被窃听和篡改。
  2. go.net/websocket的替代方案: go.net/websocket是一个相对较老的包,且其API设计在某些方面可能不如现代的WebSocket库灵活。在新的项目中,更推荐使用像gorilla/websocket这样的第三方库,它提供了更丰富的功能、更好的性能和更清晰的API。
  3. websocket.Conn.Config().Origin: 即使使用websocket.Server绕过了默认校验,你仍然可以在webHandler内部通过ws.Config().Origin获取到客户端发送的Origin头部,然后根据业务需求进行自定义验证。
  4. 错误处理: 在实际应用中,WebSocket连接的错误处理(如客户端意外断开、网络问题)至关重要。示例中增加了defer ws.Close()和更详细的错误日志。
  5. websocket.Message与fmt.Fscan: websocket.Message.Receive和websocket.Message.Send是go.net/websocket包中推荐的用于发送和接收结构化消息的方式,它们比fmt.Fscan更健壮,尤其是在处理二进制数据或更复杂的文本格式时。

总结

当在Go语言中使用go.net/websocket包构建WebSocket服务时,遇到403 Forbidden错误通常是由于websocket.Handler默认的Origin头部校验机制所致。为了解决这个问题,我们可以通过实例化websocket.Server并将其Handler字段设置为我们的业务逻辑,利用其默认的握手行为(不强制Origin校验)来成功建立连接。然而,在禁用Origin校验时,务必充分考虑安全风险,并在生产环境中采取适当的安全措施,如自定义Origin验证或使用更现代、功能更完善的WebSocket库。

以上就是Go语言WebSocket服务:解决403错误与Origin校验的详细内容,更多请关注php中文网其它相关文章!

最佳 Windows 性能的顶级免费优化软件
最佳 Windows 性能的顶级免费优化软件

每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。

下载
来源:php中文网
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn
最新问题
开源免费商场系统广告
热门教程
更多>
最新下载
更多>
网站特效
网站源码
网站素材
前端模板
关于我们 免责申明 意见反馈 讲师合作 广告合作 最新更新 English
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送
PHP中文网APP
随时随地碎片化学习

Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号