WebSocket 即时通讯前后端设计和基于token的鉴权

344 阅读2分钟

我的项目中使用的websocket 即时通讯一直没有加权限验证功能,就是简单的连接后发消息,但是最近面试的时候有人问我websocket如何鉴权,现在把这个漏洞补上
关键步奏:在第一次建立连接的时候前端需要带上token,没有token或者token失效都会连接失败,建立连接后就是长连接了,只需要验证一次就可以了

这是后端代码 go语言

路由代码

defaultRoutes.GET("/ws", func(ctx *gin.Context) {
        t := ctx.Query("token")
        token, _, err := middlewares.ParseToken(t)
        if err != nil || !token.Valid {
                ctx.JSON(400, gin.H{
                        "message": "token无效",
                })
        } else {
                controllers.UserController{}.WS(ctx.Writer, ctx.Request)
        }

})

websocket连接

var upgrader = websocket.Upgrader{
    CheckOrigin: func(r *http.Request) bool {
            return true
    },
}
var conns []*websocket.Conn
func (this UserController) WS(w http.ResponseWriter, r *http.Request) {
    c, err := upgrader.Upgrade(w, r, nil)
    if err != nil {
            println("upgrade错误:", err)
            return
    }
    defer c.Close()
    conns = append(conns, c)
    for {
            _, _, err := c.ReadMessage()
            if err != nil {
                    println("read:", err)
                    break
            }
    }
}
下面是前端代码
let token=sessionStorage.getItem("token")
const env = process.env.NODE_ENV
const url = env == 'development' ? "ws://127.0.0.1:8088/ws?token=" + token : "ws://m.liyefeng.top/ws?token=" + token

let websocket = null;
let socketState = ref(false); // 初始状态建议设为 false,等 onopen 再变 true
let isReconnecting = false;   // 核心标志位:防止并发重连
let reconnectTimer = null;    // 定时器引用,方便清理

const connect=()=> {
    if (isReconnecting) return; // 如果正在重连,直接返回
    websocket = new WebSocket(url);
    websocket.onopen = (evt) => {
        console.log("链接成功");
        socketState.value = true;
        isReconnecting = false; // 连接成功后重置标志位
        // 清除可能存在的重连定时器
        if (reconnectTimer) clearTimeout(reconnectTimer); 
    };

    websocket.onmessage = (evt) => {
        if (evt.data == "jianli") {
            refreshChartJL();
        } else if (evt.data == "music") {
            refreshChartMusic();
        }
    };

    websocket.onclose = () => {
        console.log("链接关闭");
        socketState.value = false;
        isReconnecting = false;
        attemptReconnect(); // 触发重连尝试
    };

    websocket.onerror = (err) => {
        console.error("WebSocket发生错误:", err);
        websocket.close(); // 主动关闭以触发 onclose 进入重连流程
    };
}
const attemptReconnect=()=> {
    // 如果没有 token(比如用户已退出登录),则不再重连
    if (!sessionStorage.getItem("token")) return; 
    
    if (websocket && !isReconnecting && !reconnectTimer) {
        isReconnecting = true;
        console.log("准备在 3秒 后重新连接...");
        reconnectTimer = setTimeout(() => {
            reconnectTimer = null;
            isReconnecting = false;
            connect(); // 重新调用连接函数
        }, 3000); // 3秒重连间隔
    }
}
// 不能忘记在页面销毁前清空定时器和ws对象

2026年更新前端代码,加入了断开链接自动重连