admin 管理员组

文章数量: 886992

QT POST方法:以使用腾讯云发送短信为例

    • 1、准备
    • 2、随机数
    • 3、时间戳
    • 4、哈希算法
    • 5、签名方法
    • 6、POST结合使用

POST方法是制作软件中常用的一个方法,而验证码短信在各种注册操作中需要用到,所以本文将这两个结合起来写。文章还涉及到QT随机数的生成、时间戳、哈希算法等。

1、准备

因为用到了腾讯云,所以需要按腾讯云的要求做些准备,认证什么的需要自己搞定,然后发送短信相关的签名和模板也不是很困难。https://cloud.tencent/document/product/382/37745,帮助文档里有介绍,按里面的要求来就可以了。

腾讯云提供了API Explorer进行代码的调试https://console.cloud.tencent/api/explorer?Product=sms&Version=2019-07-11&Action=SendSms&SignVersion=,这个调试工具还是很好用的。

在代码写之前,需要先看一下需要请求的参数,这个帮助文档写的头大,他把公共参数与签名操作写在了一起,请求参数在另一部分,所以需要研究一番。这个时候腾讯提供的API Explorer起作用了,这个东东把需要的参数放一起了。。。。腾讯这里有两个签名方法,他推荐使用v3,我用的是v1,诸位注意一下。
我这里列出来了:

{
    'Action' : 'SendSms',//固定
    'Language' : 'zh-CN',//固定
    'Nonce' : '',//随机数,我这里用了4位正整数
    'PhoneNumberSet.0' : '',//手机号,如果有多个手机号,从0开始数
    'Region' : '',//区域,空
    'SecretId' : '',//这个填自己的,腾讯有写怎么获取
    'SmsSdkAppid' : '',//这个填自己的,腾讯有写怎么获取
    'TemplateID' : '',//这个填自己的,腾讯有写怎么获取
    'TemplateParamSet.0' : '',//模板参数
    'TemplateParamSet.1' : '',//模板参数
    'Timestamp' : '',//时间戳
    'Version' : '2019-07-11',//版本,固定
}

我这里使用的模板是这样的:
您正在申请手机注册,验证码为:{1},{2}分钟内有效!
{1}{2},就是模板参数,申请模板的时候从1开始,代码编写从0开始。

2、随机数

QT在5.10版本之后增加了一个函数用于产生随机数,qrand()和rand()不再推荐使用。

#include <QRandomGenerator>
    
    QString randomNumber;

void MainWindow::generateRandomNumber()
{
    randomNumber = "";
    for (int i = 0; i < 4; i++)
    {
        randomNumber = randomNumber + QString("%1").arg(QRandomGenerator::global()->bounded(10));//不再使用qrand和qsrand
    }
    qDebug()<<randomNumber;
}

3、时间戳

时间戳也很常用,也很简单,腾讯写里使用时间戳,如果请求发送的时间,与标记的时间相差太长,会返回错误。

#include <QDateTime>

     int Timestamp = QDateTime::currentDateTime().toTime_t();//时间戳
     /// QT6.0中有更新,用下面这个方法
     int Timestamp = QDateTime::currentSecsSinceEpoch();//这个返回的是秒数

4、哈希算法

公共参数里面有个Signature,这个是腾讯需要的一个签名,需要用到加密算法。**使用签名方法 v1 (有时会称作 HmacSHA256 和 HmacSHA1),公共参数需要统一放到请求串中。**初学者知道怎么用就行了。https://blog.csdn/WMX843230304WMX/article/details/73649294

#include <QCryptographicHash>//哈希算法

    QByteArray arrayFromHexString;//计算哈希算法
private slots:
    void slot_GetHash_Base64(QByteArray, QByteArray);//哈希算法Base64输出

void MainWindow::slot_GetHash_Base64(QByteArray key, QByteArray baseString)//计算哈希算法,以Base64输出
{
    /*!
     * \brief Qt hmacSha1 哈希算法
     * \param key         加密需要的key
     * \param baseString  需要加密的字符串
     * \return QByteArray 加密后的数据内存中的ASCII码
     */
    arrayFromHexString = "";

    int blockSize = 64; // HMAC-SHA-1 block size, defined in SHA-1 standard
    if (key.length() > blockSize) { // if key is longer than block size (64), reduce key length with SHA-1 compression
        key = QCryptographicHash::hash(key, QCryptographicHash::Sha1);
    }
    QByteArray innerPadding(blockSize, char(0x36)); // initialize inner padding with char "6"
    QByteArray outerPadding(blockSize, char(0x5c)); // initialize outer padding with char "/"
    // ascii characters 0x36 ("6") and 0x5c ("/") are selected because they have large
    // Hamming distance (http://en.wikipedia/wiki/Hamming_distance)
    for (int i = 0; i < key.length(); i++) {
        innerPadding[i] = innerPadding[i] ^ key.at(i); // XOR operation between every byte in key and innerpadding, of key length
        outerPadding[i] = outerPadding[i] ^ key.at(i); // XOR operation between every byte in key and outerpadding, of key length
    }
    // result = hash ( outerPadding CONCAT hash ( innerPadding CONCAT baseString ) ).toBase64
    QByteArray total = outerPadding;
    QByteArray part = innerPadding;
    part.append(baseString);
    total.append(QCryptographicHash::hash(part, QCryptographicHash::Sha1));
    QByteArray hashed = QCryptographicHash::hash(total, QCryptographicHash::Sha1);
         /// https://1024tools/hmac
         /// 这一网址提供两个参考,结果A:QByteArray::fromHex(hashed.toHex()).toHex();
         /// 把结果A以Base64输出即下面的结果
         /// 注意——>把字符串hashed转换为Hex,内存中的ASCII码arrayFromHexString
         /// arrayFromHexString = QByteArray::fromHex(hashed.toHex()).toHex().toBase64()
         /// 结果B:返回原始二进制进行Base64输出:
    arrayFromHexString = QByteArray::fromHex(hashed.toHex()).toBase64().toPercentEncoding();//转换成Base64之后进行URL编码
    //qDebug()<<"hmacSha1内存中的ASCII码 arrayFromHexString \n"<<arrayFromHexString;
}

这里需要注意的是最后的注释,腾讯所需要的是下图中的结果B,上面代码块所来源的链接中的输出结果是对A进行编码,区别就在一个toHex()。

5、签名方法

整个过程中比较困难的是这个签名方法,还是推荐使用API Explorer进行调试。签名实现的过程很简单,但是需要细心调试。
1、参数排序
2、拼接请求字符串
3、拼接签名原文字符串
4、加密签名串
5、签名串编码

1、2、3比较简单,需要耐心,这里列出4、5的操作。

        this->generateRandomNumber();
        //定义输入参数
        QString Action = "SendSms";
        QString Nonce = randomNumber;//随机正整数
        QString PhoneNumberSet = "86"+ui->lineEdit_reg_tel->text();//这里很坑爹,文档里要求带“+86”,实际上腾讯自己把“+”带上了,所以只加“86”就行了
        QString SecretId = "**************";
        QString SmsSdkAppid = "*********";
        QString TemplateID = "********";
        Timestamp = QDateTime::currentDateTime().toTime_t();//时间戳
        QString Version = "2019-07-11";

        //拼接字符串
        QString str = QString("POSTsms.tencentcloudapi/?Action=%1&Language=zh-CN&Nonce=%2&PhoneNumberSet.0=%3&Region=&SecretId=%4&SmsSdkAppid=%5&TemplateID=%6&TemplateParamSet.0=%7&TemplateParamSet.1=5&Timestamp=%8&Version=%9")
                .arg(Action).arg(Nonce).arg(PhoneNumberSet).arg(SecretId).arg(SmsSdkAppid).arg(TemplateID).arg(Nonce).arg(Timestamp).arg(Version);
        //qDebug()<<"str = :   "<<str;

        //计算哈希字符串,以Base64输出,进行url编码
        QString SecretKey = "*************";
        this->slot_GetHash_Base64(SecretKey.toUtf8(), str.toUtf8());//这个toUtf8()
        QString Signature(arrayFromHexString);
        qDebug()<<"签名:\n"<<Signature;

这里输出的签名和API Explorer中的对照,一样就OK了。

6、POST结合使用

到这里就可以使用POST方法了,也很简单,这里只列出请求的方法,返回结果是JSON格式,诸位思考一下吧。

//开始请求
        QUrl url = QUrl(QString("https://sms.tencentcloudapi/"));
        QString PostData = QString("Action=%1&Language=%2&Nonce=%3&PhoneNumberSet.0=%4&Region=%5&SecretId=%6&SmsSdkAppid=%7&TemplateID=%8&Timestamp=%9&Version=%10&TemplateParamSet.0=%11&TemplateParamSet.1=5&Signature=%12")
                .arg(Action).arg("zh-CN").arg(Nonce).arg(PhoneNumberSet).arg("").arg(SecretId).arg(SmsSdkAppid).arg(TemplateID).arg(Timestamp).arg(Version).arg(Nonce).arg(Signature);
        /// 这里需要注意一下,把Signature放最后,签名串加密完之后的字符最后一般是个“=”,编码之后变成了“%3D”这样,但是这个“=”还是会影响PostData的拼接,所以把Signature放最后,诸位可以按排序进行拼接试试,腾讯会报错。
        qDebug()<<"postdata:\n"<<PostData;
        QNetworkAccessManager *manager = new QNetworkAccessManager(this);
        connect(manager, SIGNAL(finished(QNetworkReply*)), this, SLOT(slot_requestFinished_SMS(QNetworkReply*)));
        QNetworkRequest request;
        request.setUrl(url);
        request.setRawHeader("Content-Type", "application/x-www-form-urlencoded");
        manager->post(request, PostData.toUtf8());

PS:
有一些平台,不涉及金融、Money等的过程,对签名的要求比较简单,可以采用比如Md5算法等,

QString txt = content + secretKey;
QByteArray base64 = QCryptographicHash::hash(txt.toLatin1(), QCryptographicHash::Md5).toBase64();
QString data_digest(base64);

这样几行代码就可以实现加签,签名有时个会带“+”号,一些平台会提示验签不通过,此时多加一步urlencode:

QString ddata_digest = QUrl::toPercentEncoding(data_digest);

这样字符串里面的“+”"="这些就会被替换掉。

本文标签: 腾讯 为例 发送短信 方法 QT