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

Spring Boot生成SP发起的SAML请求时遇Base64编码错误求助

问题:生成SAML请求重定向时提示「SAML message was not properly base64-encoded」

我正在开发SP发起的SSO集成功能,编写了一个API用于生成SAML请求并转换为前端重定向URL,但生成URL后重定向时出现错误:SAML message was not properly base64-encoded。此前尝试使用spring-security-saml-dsl依赖生成SAMLRequest未成功,多次尝试多种方式仍无法生成可正常跳转至登录页的正确URL。

相关代码如下:

String redirectUrl = "https://login.microsoftonline.com/f8ceef31-c65g-4b4a-09e4-5f571x91255a"
        + "/saml2"
        + "?SAMLRequest=";

String samlRequest = "<?xml version=\"1.0\"encoding=\"UTF-8\"?><samlp:AuthnRequest xmlns:samlp=\"urn:oasis:names:tc:SAML:2.0:protocol\" "
        + "xmlns:saml=\"urn:oasis:names:tc:SAML:2.0:assertion\" " + "ID=\"" + Instant.now().toEpochMilli()
        + "Version=\"2.0\" " + "IssueInstant=\"" + Instant.now() + "\" "
        + "ProtocolBinding=\"urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST\" "
        + "AssertionConsumerServiceURL=\"https://login.microsoftonline.com/1ebc9492-2234-472a-87v5-ea64ae60db97/saml2\">"
        + "<saml:Issuer>https://login.microsoftonline.com/1ebc9497-2161-672a-84b4-ea64ae60db97/saml2</saml:Issuer>"
        + "<samlp:NameIDPolicy Format=\"urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified AllowCreate=\"true\"/>"
        + "</samlp:AuthnRequest>";

byte[] compressedBytes = new byte[samlRequest.length()];
Deflater deflater = new Deflater(Deflater.DEFAULT_COMPRESSION, true);
deflater.setInput(samlRequest.getBytes());
deflater.finish();
int compressedSize = deflater.deflate(compressedBytes);
deflater.end();

String base64Encoded = Base64.getEncoder()
        .encodeToString(Arrays.copyOfRange(compressedBytes, 0, compressedSize));

String relayStateString = redirectUrl + base64Encoded;

问题分析与修复步骤

1. XML语法错误导致后续处理失效

你的SAML请求XML存在多处语法问题,会直接导致压缩和编码后的数据无效:

  • <?xml version=\"1.0\"encoding=\"UTF-8\"?> 中version与encoding之间缺少空格,应为<?xml version=\"1.0\" encoding=\"UTF-8\"?>
  • ID=\"" + Instant.now().toEpochMilli() 末尾缺少闭合双引号,应为ID=\"" + Instant.now().toEpochMilli() + "\" "
  • <samlp:NameIDPolicy>标签中Format属性值未闭合,应为Format=\"urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified\" AllowCreate=\"true\"/>

2. 压缩与编码不符合SAML HTTP-Redirect绑定规范

SAML 2.0的HTTP-Redirect绑定要求:

  • 先对XML进行DEFLATE压缩(需正确处理压缩输出,避免固定长度数组截断数据)
  • 压缩后的数据先做Base64编码,再进行URL编码(标准Base64的+、/等字符在URL中会被解析异常)

修复后的代码示例

import java.io.ByteArrayOutputStream;
import java.util.Base64;
import java.util.zip.Deflater;
import java.time.Instant;

public class SamlRequestGenerator {
    public static String generateRedirectUrl() {
        // 修正XML语法,同时规范ID格式(加下划线前缀符合SAML惯例)
        String samlRequest = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
                + "<samlp:AuthnRequest xmlns:samlp=\"urn:oasis:names:tc:SAML:2.0:protocol\" "
                + "xmlns:saml=\"urn:oasis:names:tc:SAML:2.0:assertion\" "
                + "ID=\"_" + Instant.now().toEpochMilli() + "\" "
                + "Version=\"2.0\" "
                + "IssueInstant=\"" + Instant.now().toString() + "\" "
                + "ProtocolBinding=\"urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST\" "
                + "AssertionConsumerServiceURL=\"https://login.microsoftonline.com/1ebc9492-2234-472a-87v5-ea64ae60db97/saml2\">"
                + "<saml:Issuer>https://login.microsoftonline.com/1ebc9497-2161-672a-84b4-ea64ae60db97/saml2</saml:Issuer>"
                + "<samlp:NameIDPolicy Format=\"urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified\" AllowCreate=\"true\"/>"
                + "</samlp:AuthnRequest>";

        // 正确处理DEFLATE压缩,使用ByteArrayOutputStream避免数据截断
        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
        Deflater deflater = new Deflater(Deflater.DEFAULT_COMPRESSION, true);
        try {
            deflater.setInput(samlRequest.getBytes("UTF-8"));
            deflater.finish();
            byte[] buffer = new byte[1024];
            while (!deflater.finished()) {
                int count = deflater.deflate(buffer);
                byteArrayOutputStream.write(buffer, 0, count);
            }
        } catch (Exception e) {
            throw new RuntimeException("Failed to compress SAML request", e);
        } finally {
            deflater.end();
        }

        // 按规范完成Base64编码+URL编码
        byte[] compressedBytes = byteArrayOutputStream.toByteArray();
        String base64Encoded = Base64.getEncoder().encodeToString(compressedBytes);
        String urlEncodedBase64 = java.net.URLEncoder.encode(base64Encoded, java.nio.charset.StandardCharsets.UTF_8);

        // 拼接最终重定向URL
        return "https://login.microsoftonline.com/f8ceef31-c65g-4b4a-09e4-5f571x91255a/saml2?SAMLRequest=" + urlEncodedBase64;
    }
}

额外注意事项

  • SAML请求的ID建议使用UUID生成,避免时间戳重复风险
  • IssueInstant需严格遵循ISO 8601格式(Instant.now().toString()已满足要求)
  • 若尝试使用spring-security-saml-dsl,需确保正确加载IDP元数据XML文件,而非手动拼接请求参数

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.26 00:42:25