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

.NET 8.0(Linux容器)对接SSRS获取PDF字节的可靠方案咨询

问题背景

我需要在AWS ECS Linux Docker环境的.NET 8.0微服务中对接SSRS报表服务器,获取渲染后的PDF字节流。该SSRS服务器采用NTLM认证,但NTLM在Linux容器中无法正常工作。我尝试过在Docker中安装gss-ntlmssp libgssapi-krb5-2 krb5-user,但多数反馈称这并非完整可行方案;另一种方案是使用Windows容器中的代理API,但我无法部署两个服务,恳请提供可靠的解决方案。

当前实现代码
[HttpGet("render-cc-welcome-letter")]
[ProducesResponseType(typeof(FileContentResult), 200)]
[ProducesResponseType(typeof(ProblemDetails), 500)]
public async Task<IActionResult> RenderCcWelcomeLetterAsync()
{
    const string reportPath = "/Reports/Letters/Letters_AR/CC_Welcome Letter";

    var parameters = new Dictionary<string, string>
    {
        ["AssessmentDate"] = "1/4/2026",
        ["CaseID"] = "6941795",
        ["Recipients"] = "18261413"
    };

    try
    {
        // ---------------- Binding ----------------
        var binding = new BasicHttpBinding(BasicHttpSecurityMode.TransportCredentialOnly)
        {
            MaxReceivedMessageSize = int.MaxValue,
            ReaderQuotas = System.Xml.XmlDictionaryReaderQuotas.Max,
            AllowCookies = true
        };

        // 使用NTLM对接Windows认证的SSRS
        binding.Security.Transport.ClientCredentialType = HttpClientCredentialType.Ntlm;

        var endpoint = new EndpointAddress(this.reportingOptions.Url);
        var client = new ReportExecutionServiceSoapClient(binding, endpoint);

        // ---------------- 凭证配置 ----------------
        // 支持三种格式:
        // 1) Username = "domain\\user"(示例:"mydomain\\reportuser")
        // 2) Username = "user" + 可选Domain参数
        // 3) 空Username => 使用本地默认网络凭证(开发环境用)
        var username = this.reportingOptions.Username?.Trim();
        var password = this.reportingOptions.Password ?? string.Empty;

        if (!string.IsNullOrWhiteSpace(username))
        {
            if (username.Contains("\\"))
            {
                var parts = username.Split('\\', 2);
                var domain = parts[0];
                var user = parts[1];

                client.ClientCredentials.Windows.ClientCredential =
                    new System.Net.NetworkCredential(user, password, domain);
            }
            else
            {
                client.ClientCredentials.Windows.ClientCredential =
                    string.IsNullOrWhiteSpace(this.reportingOptions.Domain)
                        ? new System.Net.NetworkCredential(username, password)
                        : new System.Net.NetworkCredential(username, password, this.reportingOptions.Domain);
            }
        }
        else
        {
            client.ClientCredentials.Windows.ClientCredential =
                System.Net.CredentialCache.DefaultNetworkCredentials;
        }

        client.ClientCredentials.Windows.AllowedImpersonationLevel =
            System.Security.Principal.TokenImpersonationLevel.Impersonation;

        // 注意:.NET Core WCF中没有AllowNtlm属性,通过ClientCredentialType启用NTLM

        var trustedHeader = new TrustedUserHeader();

        // ---------------- 1) 加载报表 ----------------
        var loadResponse = await client.LoadReportAsync(
            trustedHeader,
            reportPath,
            null); // HistoryID

        var executionId = loadResponse.ExecutionHeader.ExecutionID;

        // ---------------- 2) 设置参数 ----------------
        ParameterValue[] paramArray = parameters
            .Select(p => new ParameterValue
            {
                Name = p.Key,
                Value = p.Value
            })
            .ToArray();

        await client.SetExecutionParametersAsync(
            new ExecutionHeader { ExecutionID = executionId },
            trustedHeader,
            paramArray,
            "en-us");

        // ---------------- 3) 渲染PDF ----------------
        var renderRequest = new RenderRequest
        {
            ExecutionHeader = new ExecutionHeader { ExecutionID = executionId },
            TrustedUserHeader = trustedHeader,
            Format = "PDF",
            DeviceInfo = null
        };

        var renderResponse = await client.RenderAsync(renderRequest);

        return this.File(
            renderResponse.Result,
            "application/pdf",
            "CC_WelcomeLetter.pdf");
    }
    catch (Exception ex)
    {
        var root = ex;
        while (root.InnerException != null)
        {
            root = root.InnerException;
        }

        this.logger.LogError(
            ex,
            "SSRS渲染失败。Url={Url}, User={User}, Root={RootMessage}",
            this.reportingOptions.Url,
            this.reportingOptions.Username,
            root.Message);

        return this.Problem(
            detail: $"{ex.Message} | 根错误: {root.Message}",
            statusCode: 500,
            title: "SSRS渲染错误");
    }
}
可行解决方案

方案1:改用RestSharp调用SSRS REST API(推荐)

放弃WCF客户端,使用RestSharp配合NTLM认证扩展,Linux环境下兼容性更稳定:

  1. 安装NuGet包:RestSharp 和 RestSharp.Authenticators.Ntlm
  2. 重构代码示例:
[HttpGet("render-cc-welcome-letter")]
[ProducesResponseType(typeof(FileContentResult), 200)]
[ProducesResponseType(typeof(ProblemDetails), 500)]
public async Task<IActionResult> RenderCcWelcomeLetterAsync()
{
    var reportServiceUrl = $"{reportingOptions.Url}/ReportExecution2005.asmx";
    var parameters = new Dictionary<string, string>
    {
        ["AssessmentDate"] = "1/4/2026",
        ["CaseID"] = "6941795",
        ["Recipients"] = "18261413"
    };

    try
    {
        var client = new RestClient(reportServiceUrl);
        // 配置NTLM认证
        client.Authenticator = new NtlmAuthenticator(
            reportingOptions.Domain,
            reportingOptions.Username,
            reportingOptions.Password);

        // 1. 获取ExecutionID
        var loadRequest = new RestRequest("LoadReport", Method.Post);
        loadRequest.AddParameter("Report", "/Reports/Letters/Letters_AR/CC_Welcome Letter", ParameterType.GetOrPost);
        loadRequest.AddParameter("HistoryID", null, ParameterType.GetOrPost);
        var loadResponse = await client.ExecuteAsync(loadRequest);
        // 解析XML响应提取ExecutionID(可使用XmlSerializer手动序列化处理)

        // 2. 设置报表参数
        var setParamsRequest = new RestRequest("SetExecutionParameters", Method.Post);
        // 构造参数XML并发送(需按照SSRS SOAP接口格式封装)

        // 3. 渲染PDF
        var renderRequest = new RestRequest("Render", Method.Post);
        renderRequest.AddParameter("Format", "PDF", ParameterType.GetOrPost);
        renderRequest.AddParameter("ExecutionID", executionId, ParameterType.GetOrPost);
        var pdfResponse = await client.ExecuteAsync(renderRequest);

        return File(pdfResponse.RawBytes, "application/pdf", "CC_WelcomeLetter.pdf");
    }
    catch (Exception ex)
    {
        logger.LogError(ex, "SSRS渲染失败");
        return Problem(detail: ex.Message, statusCode: 500, title: "SSRS渲染错误");
    }
}

方案2:完善Linux容器的NTLM环境配置(适配WCF)

如果坚持使用WCF客户端,需完整配置容器的NTLM依赖:

  • 修改Dockerfile,添加依赖与配置:
RUN apt-get update && apt-get install -y --no-install-recommends \
    gss-ntlmssp \
    libgssapi-krb5-2 \
    krb5-user \
    && rm -rf /var/lib/apt/lists/*

# 配置KRB5文件,指定NTLM优先级
RUN echo "[libdefaults]" > /etc/krb5.conf && \
    echo "default_realm = YOUR_DOMAIN.COM" >> /etc/krb5.conf && \
    echo "dns_lookup_realm = false" >> /etc/krb5.conf && \
    echo "dns_lookup_kdc = true" >> /etc/krb5.conf && \
    echo "[realms]" >> /etc/krb5.conf && \
    echo "YOUR_DOMAIN.COM =" >> /etc/krb5.conf && \
    echo "    kdc = dc.yourdomain.com" >> /etc/krb5.conf && \
    echo "    admin_server = dc.yourdomain.com" >> /etc/krb5.conf

# 设置环境变量启用NTLM支持
ENV COMPlus_EnableNtlm=true
ENV GSSAPI_KRB5_CONF=/etc/krb5.conf
  • 确保WCF客户端仅使用NTLM,避免Kerberos自动降级错误

方案3:修改SSRS认证方式为Basic(需权限)

如果有权限修改SSRS服务器配置,添加Basic认证简化对接:

  1. 编辑SSRS服务器的rsreportserver.config,在<AuthenticationTypes>节点中添加:
<RSWindowsBasic>
    <AuthenticationMethod>Basic</AuthenticationMethod>
    <Realm></Realm>
    <DefaultDomain>YOUR_DOMAIN</DefaultDomain>
</RSWindowsBasic>
  1. 修改代码中的认证配置:
binding.Security.Transport.ClientCredentialType = HttpClientCredentialType.Basic;
client.ClientCredentials.UserName.UserName = $"{reportingOptions.Domain}\\{reportingOptions.Username}";
client.ClientCredentials.UserName.Password = reportingOptions.Password;

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.11 16:05:09