Skip to content

百度智能云API鉴权

鉴权的主要目的是用于校验调用者的身份信息。调用千帆平台功能OpenAPI需使用基于安全认证AK/SK进行签名计算鉴权。

百度智能云鉴权简介

百度智能云提供的在线生成签名工具

1 使用 QT 计算鉴权Authorization

#ifndef ModelApiMetrics_H
#define ModelApiMetrics_H

#include <QJsonObject>
#include <QCryptographicHash>
#include <QMessageAuthenticationCode>
#include <QString>
#include <QByteArray>

#include "hv/HttpClient.h"

// 大模型服务度量指标

// 百度鉴权 https://cloud.baidu.com/doc/WENXINWORKSHOP/s/Sly8bm96d
// 百度签名计算工具 https://cloud.baidu.com/signature/index.html

class ModelApiMetrics {
public:

    // 生成百度服务度量指标
    static QMap<QString, QList<int> >GenerateBaiDuServiceMetric(
        const QString& ak, const QString& sk, const QString& appId);

private:

    // 生成百度服务的认证签名
    static QString GenerateBaiDuAuth(HttpRequestPtr& req, QString ak, QString sk, int expireSeconds = 300);

    // 使用 HMAC-SHA256 算法生成十六进制签名。
    static QString HmacSha256Hex(const QString& key, const QString& message);

    // 对字符串进行 URL 编码。
    static QString UrlEncode(const QString& src, bool encodeSlash = false);

    // 去掉字符串首尾指定的字符。
    static QString Trim(const QString& s, const QString& chars = QStringLiteral(" \t\n\r\f\v"));
};

#endif // ifndef ModelApiMetrics_H
QString ModelApiMetrics::GenerateBaiDuAuth(HttpRequestPtr& req, QString ak, QString sk, int expireSeconds)
{
    QString authStr;
    QString signTime = QDateTime::currentDateTimeUtc().toString(Qt::ISODate);
    authStr = QString("bce-auth-v1/%1/%2/%3")
              .arg(ak)
              .arg(signTime)
              .arg(expireSeconds);

    QString canonicalReq;

    // method
    req->method = HTTP_POST;
    switch (req->method) {
    case HTTP_GET:
        canonicalReq += QString("GET\n");
        break;

    case HTTP_POST:
        canonicalReq += QString("POST\n");
        break;

    case HTTP_PUT:
        canonicalReq += QString("PUT\n");
        break;

    default:
        break;
    }

    // url
    canonicalReq += QString("%1\n").arg(UrlEncode(QString::fromStdString(req->url)));

    // query string
    hv::QueryParams params = req->query_params;
    if (0 != params.size()) {
        QStringList paramList;
        for (auto& pair : params) {
            QString encodedKey = UrlEncode(Trim(QString::fromStdString(pair.first)));
            QString encodedValue = UrlEncode(Trim(QString::fromStdString(pair.second)));
            paramList.append(QString("%1=%2").arg(encodedKey, encodedValue));
        }
        std::sort(paramList.begin(), paramList.end());
        canonicalReq += QString("%1\n").arg(paramList.join("&"));
    } else {
        canonicalReq += '\n';
    }

    // header
    canonicalReq += QString("host:%1").arg(UrlEncode(QString::fromStdString(req->headers["Host"])));
    QString signKey = HmacSha256Hex(sk, authStr);
    QString signature = HmacSha256Hex(signKey, canonicalReq);

    authStr += QString("/host/%1").arg(signature);
    return authStr;
}

QString ModelApiMetrics::HmacSha256Hex(const QString& key, const QString& message)
{
    QByteArray keyBytes = key.toUtf8();
    QByteArray messageBytes = message.toUtf8();
    QByteArray hmac = QMessageAuthenticationCode::hash(messageBytes, keyBytes, QCryptographicHash::Sha256);
    return QString(hmac.toHex());
}

QString ModelApiMetrics::UrlEncode(const QString& src, bool encodeSlash)
{
    QByteArray encodedBytes;

    for (QChar c : src) {
        if ((c.isLetterOrNumber() || (c == '_') || (c == '-') || (c == '~') || (c == '.')) ||
            ((c == '/') && !encodeSlash)) {
            encodedBytes.append(c.toLatin1());
        } else {
            encodedBytes.append('%');
            QByteArray hex = QByteArray::number(c.unicode(), 16).toUpper();
            if (hex.size() == 1) {
                encodedBytes.append('0');
            }
            encodedBytes.append(hex);
        }
    }

    return QString::fromUtf8(encodedBytes);
}

QString ModelApiMetrics::Trim(const QString& s, const QString& chars)
{
    int start = 0;
    int end = s.size() - 1;
    while (start <= end && chars.contains(s[start])) {
        ++start;
    }
    while (end >= start && chars.contains(s[end])) {
        --end;
    }
    return s.mid(start, end - start + 1);
}

2 通过 Libhv 查询 文心一言 服务调用情况

查询服务调用情况

QMap<QString, QList<int> >ModelApiMetrics::GenerateBaiDuServiceMetric(
    const QString& ak, const QString& sk, const QString& appId)
{
    QMap<QString, QList<int> > resultMap;

    HttpRequestPtr req(new HttpRequest);
    req->url = "/v2/service";
    req->method = HTTP_POST;
    req->headers["Host"] = "qianfan.baidubce.com";
    req->headers["Content-Type"] = "application/json";
    req->query_params["Action"] = "DescribeServiceMetric";
    req->headers["Authorization"] = GenerateBaiDuAuth(req, ak, sk).toLocal8Bit();
    req->url = "https://qianfan.baidubce.com/v2/service";

    QDateTime   currentDateTimeUtc = QDateTime::currentDateTimeUtc();
    QDateTime   dateTime20DaysAgo = currentDateTimeUtc.addDays(-20);
    QJsonObject jsonObject;
    jsonObject["startTime"] = dateTime20DaysAgo.toString(Qt::ISODate);
    jsonObject["endTime"] = currentDateTimeUtc.toString(Qt::ISODate);
    jsonObject["appId"] = QJsonArray{ appId };
    QJsonDocument jsonDocument(jsonObject);
    QByteArray    jsonData = jsonDocument.toJson(QJsonDocument::Compact);
    req->body = jsonData;

    hv::HttpClient client;
    HttpResponse   resp;
    int ret = client.send(req.get(), &resp);
    if (ret != 0) {
        return resultMap;
    }


    QJsonParseError jsonError;
    QJsonDocument   jsonDoc = QJsonDocument::fromJson(resp.body.c_str(), &jsonError);
    if (jsonDoc.isNull() || (jsonError.error != QJsonParseError::NoError)) {
        return resultMap;
    }
    QJsonObject resultObject = jsonDoc.object();
    QJsonObject result = resultObject["result"].toObject();
    QJsonArray  serviceList = result["serviceList"].toArray();

    for (const QJsonValue& serviceVal : serviceList) {
        QJsonObject serviceObj = serviceVal.toObject();
        QString     serviceName = serviceObj["serviceName"].toString();
        QJsonArray  appList = serviceObj["appList"].toArray();
        for (const QJsonValue& appVal : appList) {
            QJsonObject appObj = appVal.toObject();
            QJsonObject metric = appObj["metric"].toObject();

            QList<int> metricsList;
            metricsList.append(metric["inputTokensTotal"].toInt());
            metricsList.append(metric["outputTokensTotal"].toInt());
            metricsList.append(metric["tokensTotal"].toInt());
            metricsList.append(metric["succeedCallTotal"].toInt());
            metricsList.append(metric["failureCallTotal"].toInt());
            metricsList.append(metric["callTotal"].toInt());

            resultMap[serviceName] = metricsList;
        }
    }
    return resultMap;
}

3 使用返回结果

void SettingsPanelWid::ServiceMetric()
{
    ConfigManager::GetInstance().WriteValue("ernie_ak", ui->ernie_ak->text());
    ConfigManager::GetInstance().WriteValue("ernie_sk", ui->ernie_sk->text());
    ConfigManager::GetInstance().WriteValue("ernie_appId", ui->ernie_appId->text());

    QMap<QString, QList<int> > resMap = ModelApiMetrics::GenerateBaiDuServiceMetric(
        ui->ernie_ak->text(), ui->ernie_sk->text(), ui->ernie_appId->text());


    QString labName = QString("lab_transfer_%1_%2");
    foreach(auto& name, resMap.keys())
    {
        int model = static_cast<int>(ModelInfoManager::GetModel(name));
        for (int i = 0; i < resMap[name].size(); i++) {
            QString labObjName = labName.arg(model).arg(i + 1);
            QLabel* lab = ui->tab_4->findChild<QLabel *>(labObjName);
            if (lab) {
                lab->setText(QString::number(resMap[name][i]));
            }
        }
    }
}