.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环境下兼容性更稳定:
- 安装NuGet包:
RestSharp和RestSharp.Authenticators.Ntlm - 重构代码示例:
[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认证简化对接:
- 编辑SSRS服务器的
rsreportserver.config,在<AuthenticationTypes>节点中添加:
<RSWindowsBasic> <AuthenticationMethod>Basic</AuthenticationMethod> <Realm></Realm> <DefaultDomain>YOUR_DOMAIN</DefaultDomain> </RSWindowsBasic>
- 修改代码中的认证配置:
binding.Security.Transport.ClientCredentialType = HttpClientCredentialType.Basic; client.ClientCredentials.UserName.UserName = $"{reportingOptions.Domain}\\{reportingOptions.Username}"; client.ClientCredentials.UserName.Password = reportingOptions.Password;
内容的提问来源于stack exchange,提问作者MrBeast PewDiePie
相关产品推荐
相关产品推荐

