Excel Add-in通过http及https对接Web服务的兼容登录问题咨询
需求背景
我正在修改一款通过Web服务接口从服务器读取数据的Excel Add-in,此前数据来源服务器不支持HTTPS协议,未来将逐步支持,但部分同用途服务器可能长期不会升级支持HTTPS。
因此我希望实现灵活的服务器登录逻辑:用户无需感知协议差异,仅需提供凭证和服务器名称即可登录,默认优先使用HTTPS协议,若目标服务器不支持则自动降级使用HTTP协议。
现有代码实现
身份验证服务封装类
public partial class AuthenticationService : System.Web.Services.Protocols.SoapHttpClientProtocol { public AuthenticationService() { this.Url = global::ExcelAddInExtension.Base.Properties.Settings.Default.ExcelAddInExtension_Base_ServiceReference_AuthenticationService; if ((this.IsLocalFileSystemWebService(this.Url) == true)) { this.UseDefaultCredentials = true; this.useDefaultCredentialsSetExplicitly = false; } else { this.useDefaultCredentialsSetExplicitly = true; } } public new string Url { get { return base.Url; } set { if ((((this.IsLocalFileSystemWebService(base.Url) == true) && (this.useDefaultCredentialsSetExplicitly == false)) && (this.IsLocalFileSystemWebService(value) == false))) { base.UseDefaultCredentials = false; } base.Url = value; } } [System.Web.Services.Protocols.SoapDocumentMethodAttribute("http://asp.net/ApplicationServices/v200/AuthenticationService/Login", RequestNamespace="http://asp.net/ApplicationServices/v200", ResponseNamespace="http://asp.net/ApplicationServices/v200", Use=System.Web.Services.Description.SoapBindingUse.Literal, ParameterStyle=System.Web.Services.Protocols.SoapParameterStyle.Wrapped)] public void Login([System.Xml.Serialization.XmlElementAttribute(IsNullable=true)] string username, [System.Xml.Serialization.XmlElementAttribute(IsNullable=true)] string password, [System.Xml.Serialization.XmlElementAttribute(IsNullable=true)] string customCredential, bool isPersistent, [System.Xml.Serialization.XmlIgnoreAttribute()] bool isPersistentSpecified, out bool LoginResult, [System.Xml.Serialization.XmlIgnoreAttribute()] out bool LoginResultSpecified) { object[] results = this.Invoke("Login", new object[] { username, password, customCredential, isPersistent, isPersistentSpecified}); LoginResult = ((bool)(results[0])); LoginResultSpecified = ((bool)(results[1])); } }
初始登录调用逻辑
using (var authenticationService = new ServiceReference.AuthenticationService()) { authenticationService.Url = "https://server/AuthenticationService.svc"; authenticationService.CookieContainer = new CookieContainer(); bool loginResult; bool loginResultSpecified; try { authenticationService.Login(userName, password, "", true, true, out loginResult, out loginResultSpecified); } catch (Exception ex) { Debug.Write(string.Format("Authentication failed, Error message: {0}, \r\nerror inner exception \r\n{1}, \r\nerror stack trace \r\n{2}.", ex.Message, ex.InnerException, ex.StackTrace))); loginResult = false; } }
问题描述
当尝试登录不支持HTTPS的服务器时,会收到预期报错:System.Net.Sockets.SocketException (0x80004005): No connection could be made because the target machine actively refused it server_ip_number:443。
最初的解决思路是在catch块中修改Url为HTTP协议重试:
authenticationService.Url = "http://server/AuthenticationService.svc"; authenticationService.Login(userName, password, "", true, true, out loginResult, out loginResultSpecified);
但误以为重试时仍返回相同的443端口拒绝连接报错,怀疑AuthenticationService对象在第一次请求后缓存了端口号。
最终解决方案
经排查,原问题是误将第一次HTTPS请求的报错日志当成了第二次HTTP重试的报错,SoapHttpClientProtocol本身不存在端口缓存机制,修改Url属性后重新发起请求会自动适配新的协议和对应端口,重试逻辑可正常生效。
修正后的完整登录逻辑如下:
using (var authenticationService = new ServiceReference.AuthenticationService()) { authenticationService.CookieContainer = new CookieContainer(); bool loginResult; bool loginResultSpecified; string serverAddr = "server/AuthenticationService.svc"; // 实际场景从用户输入获取服务器地址 // 优先尝试HTTPS authenticationService.Url = $"https://{serverAddr}"; try { authenticationService.Login(userName, password, "", true, true, out loginResult, out loginResultSpecified); } catch (Exception ex) { // 仅当HTTPS连接相关异常时降级HTTP if (ex is SocketException || ex is System.Net.WebException { Status: System.Net.WebExceptionStatus.ConnectFailure or System.Net.WebExceptionStatus.SecureChannelFailure }) { Debug.Write("HTTPS连接失败,降级使用HTTP重试"); authenticationService.Url = $"http://{serverAddr}"; try { authenticationService.Login(userName, password, "", true, true, out loginResult, out loginResultSpecified); } catch (Exception httpEx) { Debug.Write(string.Format("HTTP登录也失败,错误信息: {0}", httpEx.Message)); loginResult = false; } } else { // 非连接类异常(如账号密码错误)直接返回失败,不重试 Debug.Write(string.Format("登录失败,错误信息: {0}", ex.Message)); loginResult = false; } } }
优化建议
- 可以将成功登录的协议类型缓存到本地配置,后续同域名请求直接使用对应协议,减少重复重试的开销
- 对敏感场景可以增加HTTPS强制选项,符合安全要求的环境下关闭降级逻辑,避免HTTP传输带来的信息泄露风险
内容的提问来源于stack exchange,提问作者Markku Rintala
相关产品推荐
相关产品推荐

