
本文旨在解决Go语言使用`go.net/websocket`包时常见的403 Forbidden错误,该错误通常源于默认的`Origin`头部校验机制。我们将深入探讨`websocket.Handler`与`websocket.Server`的区别,并提供使用`websocket.Server`来绕过或自定义`Origin`校验的解决方案,确保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语言免费学习笔记(深入)”;
这个403错误通常与go.net/websocket包中websocket.Handler的默认行为有关。websocket.Handler在处理WebSocket握手时,会默认检查HTTP请求头中的Origin字段。Origin头是浏览器在发起跨域请求(包括WebSocket连接)时自动添加的,用于指示请求的来源域。
websocket.Handler的默认行为是:
这种机制主要是为了增强基于浏览器的WebSocket连接的安全性,防止跨站请求伪造(CSRF)等攻击。然而,在某些场景下,例如:
在这些情况下,websocket.Handler的默认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校验的默认握手函数,从而允许连接成功建立。
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错误
}当在Go语言中使用go.net/websocket包构建WebSocket服务时,遇到403 Forbidden错误通常是由于websocket.Handler默认的Origin头部校验机制所致。为了解决这个问题,我们可以通过实例化websocket.Server并将其Handler字段设置为我们的业务逻辑,利用其默认的握手行为(不强制Origin校验)来成功建立连接。然而,在禁用Origin校验时,务必充分考虑安全风险,并在生产环境中采取适当的安全措施,如自定义Origin验证或使用更现代、功能更完善的WebSocket库。
以上就是Go语言WebSocket服务:解决403错误与Origin校验的详细内容,更多请关注php中文网其它相关文章!
每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。
Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号