在 Qt 服务器上对用户进行身份验证

Posted

技术标签:

【中文标题】在 Qt 服务器上对用户进行身份验证【英文标题】:Authenticating users on a Qt Server 【发布时间】:2012-11-02 16:24:44 【问题描述】:

我正在尝试使用 C++/QtTcpSocket 为个人项目(多人国际象棋游戏)实现身份验证系统。

我的朋友建议了一种验证用户的方法,但我想问是否有更简单或更好的方法。来自 Python 背景,主要从事这个项目是为了加深对 C++ 的理解。

我会发布我朋友建议的方法,并寻求更好的解决方案。

他以一种伪代码方式构建它。服务器基本搭建好了,现在希望实现认证

*干杯

void process_packet(PACKET *pkt)

    switch(pkt->PacketID)
    
        case 0: // let's say packet id 0 is the logon packet; packet contents are username and password
        
            //let's say packet size is 101 bytes; packet id was already received, so get the other 100 bytes
            unsigned char BUFFER[101] = 0; // i always add an extra byte to the end of the buffer to allow for off-by-one errors ^_^

            int result = recv_packet(pkt->cSocket, 100, BUFFER);

            if(result <= 0)
                return; // connection error; no packet data was received

            unsigned char *UserName = BUFFER+0; //+0 is not neccessary, but the username starts at the beginning. just getting the point across.
            unsigned char *PassWord = BUFFER+50;

            //side note: if we did "unsigned long *blah = BUFFER+4" or something, we would have to make sure the byte order is right. network byte order is BIG ENDIAN
            //  WINDOWS byte order is LITTLE ENDIAN

            result = QueryDatabase("SELECT username, password FROM chess_players WHERE username = '%s'", FILTER_INVALID_CHARS(UserName));

            // check result

            unsigned char ServerResponse[2] = 0;

            if(result['password'] == PassWord)
            
                ServerResponse[0] = 1; // packet id will be 1. the next byte can be 1 or 0 to indicate logon success or failure.
                ServerResponse[1] = true; // so packet 0x0101 mean logon success, packet 0x0100 means logon failure
                send_packet(pkt->cSocket, ServerResponse, 2);
             else 

                ServerResponse[0] = 1;
                ServerResponse[1] = false;
                send_packet(pkt->cSocket, ServerResponse, 2);
            
        
        break;

        default:
        
            // received an unknown packet id; send a packet to the client that indicates an error_status_t

            unsigned char *ServerResponse[2] = 0;
            ServerResponse[0] = 2; // packet id 2 means server error
            ServerResponse[1] = 0; // error code 0 means 'unknown packet id'

            send_packet(pkt_cSocket, ServerResponse, 2);
        
        break;
    

    delete pkt; // must delete pkt, was created with 'new' in get_client_packets()

【问题讨论】:

【参考方案1】:

这看起来相当 C 风格,不像 Qt 的做事方式。 您的问题没有一般性的答案,但我的建议如下:

收听QTcpServernewConnection() 信号。您的处理程序必须调用nextPendingConnection() 才能让下一个客户端在队列中等待。您要做的第一件事可能是您的用户身份验证。 一旦通过身份验证,您将 QTcpSocket 保留在您的活动连接列表中。

看看例如财富客户端/服务器示例如何实际写入/读取数据包。 您可能还想查看流运算符&lt;&lt; 来序列化您的对象。这比您发布的低级方法更容易且不易出错。此外,QDataStream 将自动处理主机和网络字节顺序。

【讨论】:

【参考方案2】:

如果您遵循了财富客户端/服务器示例,您应该有一个 QTcpServer (Rfserver) 和一个 QThread 子类(Rfdevice,它的实例变量在下面的代码中称为thread),其中包含一个 QTcpSocket (listenSocket)。

话虽如此,在您的服务器类中,侦听传入连接,我的设置如下所示:

void Rfserver::incomingConnection(int socketDescriptor)
    if(thread) //if thread exists, there is probably still an open connection
        if(thread->listenSocket)//if thread exists and the listenSocket is filled, there is definately an open connection
            if(thread->listenSocket->state() == QAbstractSocket::UnconnectedState) 
                //but alas, it could just be in the unconnected state, if so kill it.
                this->disconnect();
                thread->terminate();
                thread=0;
                connected=false;
            //otherwise, do nothing, because the software is happily connected to a device
        
    
    if(!thread)    //if no thread exists, we are by no means connected
        thread = new rfdevice(socketDescriptor, this); //set up a new thread
        //this first connection communicates the string from your socket to the server parent...use it if you want.
        connect( thread, SIGNAL(RemoteButton(QString)),this,SLOT(remoteButton(QString)),Qt::BlockingQueuedConnection); 
        connect( thread, SIGNAL(error(QTcpSocket::SocketError)),this,SLOT(tcpError(QTcpSocket::SocketError)),Qt::AutoConnection);
        connect( thread, SIGNAL(finished()), this, SLOT(threadZero())); //I have a threadZero function that deletes all the data then schedules the socket for deletion.
        thread->start(); 
        connected=true;
        QString *welcome = new QString("Enter your password:\r\n");
        echoCommand(welcome); //this is a function you will implement that sends the welcome message to the pending device.
    

好的,现在,当设备尝试连接到服务器时,设备会显示"Enter your password:\r\n"。您的设备可能会使用密码和用户名对此做出响应。但 Qt 的一面看起来像这样:

/*
 FUNCTION:read
    this is a polling runloop that listens for data as long as the socket is connected or connecting.  If a
 write is ever scheduled, it will be called from this runloop..
 */
void Rfdevice::read(void)
    while((listenSocket->state() == QAbstractSocket::ConnectedState) || (listenSocket->state() == QAbstractSocket::ConnectingState))
        //if there is data available to send write it to the socket
        if(dataToSend) this->write();
        if(listenSocket->waitForReadyRead(50)) readBytes(); 
        //wait for 50ms for data from the device
        //if there is ever data available to be read, read it.
    

您的设备以username---password\r\n 格式的用户名/密码进行响应。然后套接字会这样做:

/*
FUNCTION:readBytes
this is like a callback function because it only gets called when there is data available for read.
It basically converts the data to a string.
 */
void Rfdevice::readBytes(void)
    QByteArray newData;
    newData = listenSocket->readAll();
    QString *recieved = new QString(newData);
    QStringList userAndPass = recieved.split("---");//this is your delimiter
    QString username = userAndPass.at(0);
    QString password = userAndPass.at(1);

    //NOW, check the username and password vs your SQL or wherever it's saved.


伪代码在细节上相当完整。希望你能把它们放在一起!如果您需要更多代码,请告诉我。

【讨论】:

甜心帮了大忙 :-)

以上是关于在 Qt 服务器上对用户进行身份验证的主要内容,如果未能解决你的问题,请参考以下文章

在 mongo、node、passport 和 heroku 上对用户进行身份验证。几天后停止工作

在 Angular 2/4 应用程序上对用户进行身份验证后的动态角色和权限

使用 o-auth 在我的 API 上对用户进行身份验证

使用 Google Apps 帐户登录时,在 Android 上对 Google Play 游戏服务进行身份验证会导致错误

如何允许在阻止第三方 cookie 的浏览器上对 web 应用程序进行身份验证?

通过 R Studio Server 使用 BigQuery 在 Google Compute Engine 上对服务帐户进行身份验证