笔记

  在平时上网时,我们登陆一些网站后,为了校验客户端的身份、保障数据的安全性,服务器会给浏览器发送一个token值,这个token值就是一张令牌,你可以把它看成一张通行证,有了它你才能对该网站进行提交数据、查询数据等操作,并且很大程度上,它能保障客户端与服务器数据连接的安全性。

一般(多数)情况下,token值都是有期限的,也就是它在一定时间内有效,超过这个设定时间,就需要重新获取新的token值。当然,也有无状态的token,它可以允许你在多个服务间共享。


原理

1、登陆获取token

1191937.jpg
(图源自互联网,若有侵权,请告知删除)

在QT模拟网页进行登陆操作时,无论是用getPOST方式,登陆成功之后,需要将服务器返回的token值保存下来,以便后面使用。以上一篇文章QT客户端与JAVA服务器的HTTPS通信为例,这里仅写一下关于token获取部分,相信熟悉QT操作JSON数据的同学,这点是非常简单的,其他代码可以参考该例子。

void Widget::finishedSlot_Registered(QNetworkReply *registered)
{
    if (registered->error() == QNetworkReply::NoError)
    {
        // 获取响应信息
        QByteArray bytes = registered->readAll();      //读取所有字节;
        QJsonParseError jsonError;
        //转化为JSON文档
        QJsonDocument doucment = QJsonDocument::fromJson(bytes, &jsonError);
        // 解析Json  error
        if (doucment.isObject()) {
            QJsonObject obj = doucment.object();
            QJsonValue val;
            QJsonValue data_value;
            //解析Json对象
            if(data_obj.contains("token"))      //获取用户登陆的token
            {
                token_val = data_obj.value("token");
                qDebug() << tr("打印token");
                qDebug() << token_val;
            }
            if(data_obj.contains("username"))   //获取用户名;
            {
             username = QString(data_obj.value("username").toString());
             ui->textBrowser->appendPlainText(tr("登陆用户名为:")+username);
                qDebug() << tr("打印username");
                qDebug() << username;
            }
            if (data_obj.contains("user_id")) {     //获取用户登陆ID
            userid =QString::number(data_obj.value("user_id").toInt());
            ui->textBrowser->appendPlainText(tr("登陆用户ID为:")+userid);
               qDebug() << tr("打印userid");
               qDebug() << userid;
           }
        }
    }
}

这里关键是获取到服务器返回的token


2、业务请求

1192006.jpg
(图源自互联网,若有侵权,请告知删除)

成功登陆拿到token 之后,后面所有的请求操作,都必须带上该token值,否则服务器会拒绝连接。那么,在QT中该如何带上token值呢?其实原理跟网页上的请求是一样的,带在请求头部。

示例:


...
QString Url_serial = "https://www.example/api/";
QUrl serviceUrl(Url_serial);
QNetworkRequest request_registered(serviceUrl);

// 设置SSL认证方式
QSslConfiguration sslconfig;
sslconfig.setPeerVerifyMode(QSslSocket::VerifyNone);
sslconfig.setProtocol(QSsl::TlsV1_2);
//sslconfig.setPeerVerifyDepth(1);

//设置本地证书
QFile keyFile("client.p12");
bool openOK = keyFile.open(QIODevice::ReadWrite);

QSslKey key;
QSslCertificate certs;
QList<QSslCertificate> caCerts;
QByteArray passPhrase = QString("12345678").toLatin1();
openOK = QSslCertificate::importPkcs12(&keyFile, &key, &certs, &caCerts, passPhrase);
keyFile.close();

request_registered.setSslConfiguration(sslconfig);
request_registered.setRawHeader("Accept","*/*");
request_registered.setRawHeader("Connection","keep-alive");

//设置 token 登陆凭证;
request_registered.setRawHeader(QByteArray("Authorization"),QByteArray(getQJsonDocumentFromQJsonValue(token_val)));
request_registered.setHeader(QNetworkRequest::UserAgentHeader,"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_9_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/33.0.1750.152 Safari/537.36");
request_registered.setHeader(QNetworkRequest::ContentTypeHeader, "application/x-www-form-urlencoded");

//组装数据
QUrlQuery postData1;
QByteArray serial_check1((char*)Serial_num, 16);
serial_check = serial_check1.toHex();
postData1.addQueryItem("seq",serial_check);

qDebug() << tr("提交的用户token:") + getQJsonDocumentFromQJsonValue(token_val);

//发起网络请求
QNetworkReply* check = m_accessManager_Registered->post(request_registered,postData1.toString(QUrl::FullyEncoded).toUtf8());
....

比较容易犯错的地方是request_registered.setRawHeader(QByteArray("Authorization"),QByteArray(getQJsonDocumentFromQJsonValue(token_val))) 中,第二个参数的值,也就是携带的token必须是QByteArray类型,而从服务器获取到的是JSON类型,所以需要做一个转换;

转换Demo示例:

QByteArray Widget::getQJsonDocumentFromQJsonValue(QJsonValue jsonString)
{
    QByteArray authon;
    QJsonValue vals = jsonString;
    if(vals.isObject())
    {
        QJsonDocument doc(vals.toObject());
        authon = doc.toBinaryData();
    }
    else
    {
        authon = vals.toString().toUtf8();
    }
    return authon;

}

这样,就完成了一次携带token值的业务数据请求,后面所有的操作都需要携带这个。建议把它封装在请求头的类里面,方便可以直接使用。


Token过期,刷新Token

1192054.jpg
(图源自互联网,若有侵权,请告知删除)

前面提到,如果超出设定时间,token值已经过期了,那就无法进行操作了,这时就需要用户重新登陆,获取一个新的token值,这一步可以写在网络请求的响应函数里,通过服务器返回的JSON字段进行判断,如果服务器判定为token过期,那么就重新跳转到登陆,进行登陆操作,同时务必要清空保存token值的变量,对它重新赋值,以保证它是最新的。

总结

这篇文章讲到如何保存、转化和携带token值进行网络请求的操作,在QT网络编程中,这不算很难的知识点,但是确很容易出错,谨以此文章做为笔记,也希望能够帮助因此困惑的人,如果本文给您带来帮助,欢迎点赞,评论,转发,打赏。



——The End

最后修改:2020 年 05 月 15 日 02 : 04 PM
您的支持就是我持续更新的动力!