Files
nl-im-service/internal/turnserver/server.go

121 lines
3.5 KiB
Go
Raw Normal View History

2025-12-02 21:00:26 +08:00
/**
* package turnserver
* 作用基于 pion/turn 封装内置的 WebRTC 中继服务
* 特性
* 1. 支持 UDP TCP 协议
* 2. 手动实现了长期凭证机制 (Long-Term Credential Mechanism) 进行鉴权
* 3. 支持分布式部署通过共享密钥验证 Token
*/
package turnserver
import (
"crypto/hmac"
"crypto/sha1"
"encoding/base64"
"fmt"
"log"
"net"
"strconv"
"time"
"github.com/pion/turn/v2"
"github.com/spf13/viper"
)
/**
* Start
* 功能初始化并启动 TURN 服务器
* 注意这是一个阻塞调用通常建议在 goroutine 中运行
*/
func Start() {
if !viper.GetBool("turn.enabled") {
log.Println(" [TURN] 服务已禁用")
return
}
publicIP := viper.GetString("turn.public_ip")
port := viper.GetInt("turn.listen_port")
realm := viper.GetString("turn.realm")
// 监听 UDP 端口
udpListener, err := net.ListenPacket("udp4", "0.0.0.0:"+strconv.Itoa(port))
if err != nil {
log.Fatalf("❌ [TURN] UDP 监听失败: %v", err)
}
// 定义鉴权回调函数
// 每次客户端发起 Allocate 请求时触发
authHandler := func(username, realm string, srcAddr net.Addr) ([]byte, bool) {
// 1. (可选) 验证 username 时效性
// username 格式: timestamp:userid
// 生产环境可以解析 timestamp 检查是否过期
// 2. 根据共享密钥重新计算密码 (HMAC-SHA1)
secret := viper.GetString("turn.shared_secret")
password := generateHMACPassword(username, secret)
// 3. 生成 HA1 Key
// pion/turn 的 AuthHandler 需要返回 MD5(username:realm:password) 作为 Key
// 这是 STUN/TURN 标准的完整性校验要求
key := turn.GenerateAuthKey(username, realm, password)
return key, true
}
// 创建 TURN Server 实例
s, err := turn.NewServer(turn.ServerConfig{
Realm: realm,
AuthHandler: authHandler,
// 配置中继地址生成器 (使用公网IP否则客户端无法连接)
PacketConnConfigs: []turn.PacketConnConfig{
{
PacketConn: udpListener,
RelayAddressGenerator: &turn.RelayAddressGeneratorStatic{
RelayAddress: net.ParseIP(publicIP),
Address: "0.0.0.0",
},
},
},
})
if err != nil {
log.Fatalf("❌ [TURN] 启动失败: %v", err)
}
log.Printf("🚀 [TURN] 服务已启动 | IP: %s | Port: %d | Realm: %s", publicIP, port, realm)
// 阻塞保持服务运行
// 在 main.go 中,我们依靠主线程的 select 或 Web Server 阻塞,所以这里不需要 block
// 但为了代码独立性如果是独立进程需要阻塞。这里因为是库函数只需返回即可Server 会在后台运行。
_ = s
}
/**
* GenerateCredentials
* 功能为特定用户生成 TURN 服务的访问凭证
* 算法WebRTC 标准的 Long-Term Credential Mechanism (HMAC-SHA1)
* @param userID 用户ID
* @return username, password (credential)
*/
func GenerateCredentials(userID string) (string, string) {
// Username = timestamp:userid (有效期 24 小时)
timestamp := time.Now().Add(24 * time.Hour).Unix()
username := fmt.Sprintf("%d:%s", timestamp, userID)
secret := viper.GetString("turn.shared_secret")
// 生成密码
password := generateHMACPassword(username, secret)
return username, password
}
/**
* generateHMACPassword
* 功能辅助函数使用 HMAC-SHA1 算法生成密码
* 标准Base64(HMAC-SHA1(secret, username))
*/
func generateHMACPassword(username, secret string) string {
mac := hmac.New(sha1.New, []byte(secret))
mac.Write([]byte(username))
return base64.StdEncoding.EncodeToString(mac.Sum(nil))
}