【问题标题】:SocketIO + MySQL AuthenticationSocketIO + MySQL 认证
【发布时间】:2025-11-23 17:50:01
【问题描述】:

我正在尝试通过 socketIO 对 MySQL 数据库进行身份验证。我已经建立了连接并且可以毫无问题地查询结果,但由于某种原因,我无法将用户是否通过身份验证传递给 socketio 的connection 部分。这个想法是我的应用程序有主机和观众。如果连接到应用程序而不在QueryString 中发送密码,则应用程序假定它是一个查看器并接受连接。如果发送了密码,则会针对 DB 进行检查并接受/拒绝连接。我想要一个变量传递给connection,这样我就可以在我的应用程序事件中使用它。这是我到目前为止所拥有的,但显然 data.query['ishost'] 没有传递到应用程序中。

sio.configure(function() {
    sio.set('authorization', function (data, accept) {
        UserID = data.query['username'];

        try {
            UserID = UserID.toLowerCase();
        } catch(err) {
            return accept("No WebBot Specified. ("+err+")", false);
        }

        // if not sending a password, skip authorization and connect as a viewer
        if (data.query['password'] === 'undefined')
        {
            return accept(null, true);
        }
        // if sending a password, attempt authorization and connect as a host
        else
        {
            client.query(
            'SELECT * FROM web_users WHERE username = "'+UserID+'" LIMIT 1',
              function selectCb(err, results, fields) {
                if (err) {
                  throw err;
                }
                // Found match, hash password and check against DB
                if (results.length != 0)
                {
                    // Passwords match, authenticate.
                    if (hex_md5(data.query['password']) == results[0]['password'])
                    {
                        data.query['ishost'] = true;
                        accept(null, true);
                    }
                    // Passwords don't match, do not authenticate
                    else
                    {
                        data.query['ishost'] = false;
                        return accept("Invalid Password", false);
                    }
                }
                // No match found, add to DB then authenticate
                else
                {
                    client.query(
                        'INSERT INTO web_users (username, password) VALUES ("'+UserID+'", "'+hex_md5(data.query['password'])+'")', null);

                    data.query['ishost'] = "1";
                    accept(null, true);
                }

                client.end();
              }
            );

            // Should never reach this
            return accept("Hacking Attempt", false);
        }

        // Definitely should never reach this
        return accept("Hacking Attempt", false);
    });
});

写入data.query 使其可以通过握手数据访问。但由于某种原因,它没有通过应用程序传递它。任何帮助表示赞赏,谢谢。

【问题讨论】:

    标签: node.js authentication websocket socket.io


    【解决方案1】:

    你很接近,但我建议设置请求标头而不是设置查询字符串参数。您的授权函数中的data 变量是握手数据,其中包含您可以使用的请求标头和cookie 信息。以下是设置 cookie 的示例:

    在服务器上

    io.configure(function() {
        io.set('authorization', function(handshake, callback) {
            var cookie, token, authPair, parts;
    
            // check for headers
            if (handshake.headers.cookie && 
                handshake.headers.cookie.split('=')[0]=='myapp') {
    
                // found request cookie, parse it
                cookie   = handshake.headers.cookie;
                token    = cookie.split(/\s+/).pop() || '';
                authPair = new Buffer(token, 'base64').toString();
                parts    = authPair.split(/:/);
    
                if (parts.length>=1) {
                    // assume username & pass provided, check against db
                    // parts[0] is username, parts[1] is password
                    // .... {db checks}, then if valid....
                    callback(null, true);
                } else if(parts.length==1) {
                    // assume only username was provided @ parts[0]
                    callback(null,true);
                } else {
                    // not what we were expecting
                    callback(null, false);
                }
            }
            else {
                // auth failed
                callback(null, false);
            }
        });
    });
    

    在客户端

    你打电话给socket.connect之前,用你的授权/用户信息设置一个cookie:

    function writeCookie(value, days) {
        var date, expires;
    
        // days indicates how long the user's session should last
        if (days) {
            date = new Date();
            date.setTime(date.getTime()+(days*24*60*60*1000));
            expires = "; expires="+date.toGMTString();
        } else {
            expires = "";
        }
        document.cookie = "myapp="+Base64.encode(value)+expires+"; path=/";
    };
    
    // for a 'viewer' user:
    writeCookie('usernameHere', 1);
    
    // for the 'host' user:
    writeCookie('usernameHere:passwordHere', 1);
    

    除非您的浏览器支持btoa(),否则您需要在客户端使用 Base64 库。

    请务必注意,这不是一个好的身份验证结构。直接在查询字符串或标头信息中传递用户凭据是不安全的。不过,这种方法让您更接近更安全的方法。我建议查看像 passport.js 或everyauth 这样的身份验证库。您可以在此代码中添加子代码,以利用这些库在运行检查时存储的会话信息。

    【讨论】:

    • 效果很好。是的,我会调查一下passport.js。感谢您的帮助。
    • 我不确定,我通常会避免使用 Flash Sockets 来简化我的服务器配置