You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

C++开发SAML SP:如何压缩/解压URL中的XML?

解决C++中SAML请求XML的压缩与编码问题

嘿,我来帮你搞定SAML请求压缩这个难题!SAML规范里明确要求,通过GET方式传递的SAMLRequest参数需要经过DEFLATE压缩 + Base64编码 + URL编码这三步(其中URL编码是为了安全传输,避免特殊字符破坏URL结构)。你已经掌握了Base64,那核心的压缩部分我来给你详细讲清楚。

关键知识点

SAML标准规定的压缩方式是原始DEFLATE算法(注意不是gzip,gzip会额外添加文件头和尾,不符合SAML要求)。我们可以用C++常用的zlib库来实现这个压缩。

完整实现步骤及代码

1. 依赖准备

首先确保你的项目链接了zlib库,编译时需要加上-lz参数(比如GCC编译命令:g++ your_code.cpp -o your_app -lz)。

2. DEFLATE压缩函数

这个函数会把UTF-8编码的XML字符串压缩成原始DEFLATE二进制数据:

#include <zlib.h>
#include <string>
#include <vector>
#include <stdexcept>
#include <cstring>
#include <cstdio>
#include <cctype>

std::vector<unsigned char> deflate_compress(const std::string& input) {
    z_stream zs;
    std::memset(&zs, 0, sizeof(zs));

    // 初始化压缩器,-MAX_WBITS表示使用原始DEFLATE(无gzip头)
    if (deflateInit2(&zs, Z_DEFAULT_COMPRESSION, Z_DEFLATED, -MAX_WBITS, 8, Z_DEFAULT_STRATEGY) != Z_OK) {
        throw std::runtime_error("Failed to initialize deflate compressor");
    }

    zs.next_in = reinterpret_cast<Bytef*>(const_cast<char*>(input.data()));
    zs.avail_in = input.size();

    std::vector<unsigned char> output_buffer;
    char temp_buffer[32768]; // 32KB缓冲区,平衡性能和内存占用

    do {
        zs.next_out = reinterpret_cast<Bytef*>(temp_buffer);
        zs.avail_out = sizeof(temp_buffer);

        int ret = deflate(&zs, Z_FINISH);
        if (ret != Z_OK && ret != Z_STREAM_END) {
            deflateEnd(&zs);
            throw std::runtime_error("Deflate compression failed with code: " + std::to_string(ret));
        }

        size_t bytes_written = sizeof(temp_buffer) - zs.avail_out;
        output_buffer.insert(output_buffer.end(), temp_buffer, temp_buffer + bytes_written);
    } while (zs.avail_out == 0);

    deflateEnd(&zs);
    return output_buffer;
}

3. Base64编码函数

这里提供一个标准Base64实现(你如果有现成的可靠实现也可以直接用):

std::string base64_encode(const std::vector<unsigned char>& data) {
    const std::string base64_chars = 
        "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
        "abcdefghijklmnopqrstuvwxyz"
        "0123456789+/";

    std::string result;
    int i = 0;
    unsigned char char_array_3[3];
    unsigned char char_array_4[4];

    for (unsigned char c : data) {
        char_array_3[i++] = c;
        if (i == 3) {
            char_array_4[0] = (char_array_3[0] & 0xfc) >> 2;
            char_array_4[1] = ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4);
            char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6);
            char_array_4[3] = char_array_3[2] & 0x3f;

            for (i = 0; i < 4; i++) {
                result += base64_chars[char_array_4[i]];
            }
            i = 0;
        }
    }

    // 处理剩余不足3字节的情况
    if (i > 0) {
        for (int j = i; j < 3; j++) {
            char_array_3[j] = '\0';
        }

        char_array_4[0] = (char_array_3[0] & 0xfc) >> 2;
        char_array_4[1] = ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4);
        char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6);
        char_array_4[3] = char_array_3[2] & 0x3f;

        for (int j = 0; j < i + 1; j++) {
            result += base64_chars[char_array_4[j]];
        }

        while (i++ < 3) {
            result += '=';
        }
    }

    return result;
}

4. URL编码函数

Base64中的+、/、=都是URL特殊字符,必须转义才能安全传递:

std::string url_encode(const std::string& input) {
    std::string result;
    for (char c : input) {
        if (c == '+') {
            result += "%2B";
        } else if (c == '/') {
            result += "%2F";
        } else if (c == '=') {
            result += "%3D";
        } else if (std::isalnum(c) || c == '-' || c == '_' || c == '.' || c == '~') {
            // 这些字符是URL安全的,直接保留
            result += c;
        } else {
            // 其他特殊字符按URL编码规则转义
            char buf[10];
            std::snprintf(buf, sizeof(buf), "%%%02X", static_cast<unsigned char>(c));
            result += buf;
        }
    }
    return result;
}

5. 整合使用示例

把上面的函数整合起来,生成符合要求的SAML请求URL:

int main() {
    // 替换成你的实际SAML请求XML
    std::string saml_xml = R"(<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<samlp:AuthnRequest xmlns:dsig="http://www.w3.org/2000/09/xmldsig#" xmlns:enc="http://www.w3.org/2001/04/xmlenc#" xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion" xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol" ID="your-unique-id" Version="2.0" IssueInstant="2024-05-20T12:00:00Z" Destination="http://acme.com:16006/idp/samlv20" ProtocolBinding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" AssertionConsumerServiceURL="http://your-sp-domain.com/acs">
    <saml:Issuer>http://your-sp-domain.com</saml:Issuer>
    <samlp:NameIDPolicy Format="urn:oasis:names:tc:SAML:2.0:nameid-format:transient" AllowCreate="true"/>
</samlp:AuthnRequest>)";

    try {
        // 步骤1:DEFLATE压缩XML
        auto compressed_data = deflate_compress(saml_xml);
        // 步骤2:Base64编码压缩后的数据
        std::string base64_result = base64_encode(compressed_data);
        // 步骤3:URL编码Base64结果
        std::string url_safe_result = url_encode(base64_result);

        // 构造最终的请求URL
        std::string final_url = "http://acme.com:16006/idp/samlv20?SAMLRequest=" + url_safe_result;
        std::cout << "最终请求URL:\n" << final_url << std::endl;
    } catch (const std::exception& e) {
        std::cerr << "处理出错:" << e.what() << std::endl;
        return 1;
    }

    return 0;
}

注意事项

  • 确保你的XML是UTF-8编码,SAML标准强制要求这一点。
  • 压缩时必须使用原始DEFLATE模式(通过-MAX_WBITS参数设置),不能用gzip,否则IDP会无法解析。
  • 如果你用的是第三方Base64库,要确认是标准Base64,不是URL安全的Base64(之后再单独做URL编码更稳妥)。

内容的提问来源于stack exchange,提问作者gaurav

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.27 03:54:36