RAD Server 10.3.1 64位编译下TEMSClientAPI认证失败求助
问题背景与故障现象
- 基于Delphi 10.3.1开发的RAD Server Web应用,原32位部署运行正常,近期编译为64位部署至Windows Server的Apache后,多数功能正常,但Rest.Backend.EMSApi.TEMSClientAPI认证流程失败
- 核心故障点:执行代码
LEMSClientAPI.QueryUserName(LAgency + '.' + LUserName, LUser)时触发错误 - 对比验证:在EMSDevServer(未加密环境)中运行时认证完全正常;Apache配置无明显问题,已认证用户可正常访问非认证端点
- 初步疑点:查阅资料提示可能存在TLS版本不匹配,但不清楚如何针对Delphi组件开展排查
相关代码
procedure TCustomLogonResource.PostLogin(const AContext: TEndpointContext; const ARequest: TEndpointRequest; const AResponse: TEndpointResponse); var LEMSAPI : TEMSInternalAPI; LEMSClientAPI : Rest.Backend.EMSApi.TEMSClientAPI; LResponse : IEMSResourceResponseContent; LValue : TJSONValue; LUserName : string; LPassword : string; ObjCons : TJSONObject; ObjID : String; LUser : REST.Backend.EMSApi.TEMSClientAPI.TUser; LLogin : REST.Backend.EMSApi.TEMSClientAPI.TLogin; LAgency : String; myJSON : TJSONObject; myUsers : TArray<string>; ARec : TEMSClientAPI.TUpdatedAt; AInfo : TEMSClientAPI.TConnectionInfo; LEID : String; LFullName : String; LProfile : String; LModuleName : String; LToken : String; iPos : Integer; LOTP : String; LOTU : String; sList: TStringList; tempJSONArray: TJSONArray; tempJSONValue: TJSONValue; tempJSONObject: TJSONObject; tempStream: TStream; i: Integer; slDiag: TStringList; begin ObjCons := nil; tempJSONArray := nil; slDiag := nil; LModuleName := ''; LToken := ''; iPos := 0; LOTP := ''; LOTU := ''; myJSON := TJSONObject.Create; // Create in-process EMS API LEMSAPI := TEMSInternalAPI.Create(AContext); AInfo.BaseURL := 'https://www.notmyrealurl.com/api/'; AInfo.MasterSecret := '**************'; LEMSClientAPI := Rest.Backend.EMSApi.TEMSClientAPI.Create(); LEMSClientAPI.ConnectionInfo := AInfo; LEMSClientAPI.Authentication := REST.Backend.EMSApi.TEMSClientAPI.TAuthentication.MasterSecret; try Try // Extract credentials from request if not (ARequest.Body.TryGetValue(LValue) and LValue.TryGetValue<string>(TEMSInternalAPI.TJSONNames.UserName, LUserName) and LValue.TryGetValue<string>(TEMSInternalAPI.TJSONNames.Password, LPassword)) then AResponse.RaiseBadRequest('', 'Missing credentials') LAgency := ANSILowerCase(Trim(LeftStr(LUserName, Pos('.', LUserName) - 1))); ARequest.Body.TryGetObject(tempJSONObject); tempJSONObject.TryGetValue<string>('module_name', LModuleName); tempJSONObject.TryGetValue<string>('otu', LOTU); tempJSONObject.TryGetValue<string>('otp', LOTP); // if the username are valid user in the Agency's Users Table in their Agency DB, then proceed if ValidateCredentials('', LPassword, LModuleName, LOTU, LOTP, LUserName, LEID, LFullName, LProfile, LToken) then begin // if not using OTP/OTU if LOTU = '' then begin sPlace := 'Before LEMSClientAPI'; // if the user doesn't exist in the EMS database, we need to add him if not LEMSClientAPI.QueryUserName(LAgency + '.' + LUserName, LUser) then begin LEMSClientAPI.SignupUser(LAgency + '.' + LUserName,LPassword,nil,LLogin); end // if the user already exists in the EMS database else begin ObjId := LUser.UserID; ObjCons := TJSONObject.Create; ObjCons.AddPair('password', LPassword); LEMSClientAPI.UpdateUser(LUser,ObjCons,ARec); end; end; // end if not using OTU/OTP if LToken = '' then begin LResponse := LEMSAPI.LoginUser(LAgency + '.' + LUserName, LPassword); LEMSClientAPI.LoginUser(LAgency + '.' + LUserName,LPassword, LLogin); UpdateSessionToken(LAgency + '.' + LUserName,LLogin.AuthToken); end; iPos := Pos('.', LUserName); myJSON.AddPair('agency_key',LAgency); myJSON.AddPair('user',LUserName); myJSON.AddPair('username',LAgency + '.' + LUserName); myJSON.AddPair('eid',LEID); myJSON.AddPair('fullname',Trim(LFullName)); myJSON.AddPair('profile',Trim(LProfile)); myJSON.AddPair('sessionToken',IfThen(LToken = '', LLogin.AuthToken, LToken)); myJSON.AddPair('module_name ',LModuleName); AResponse.StatusCode := 200; end else begin myJSON.AddPair('module_name',LModuleName); myJSON.AddPair('Authentication Failed','Invalid username or password'); AResponse.StatusCode := 400; end; except on e: exception do begin raise Exception.Create(e.Message); end; end; finally AResponse.Body.SetValue(myJSON,true); LEMSAPI.Free; ObjCons.Free; slDiag.Free; end; end;
排查思路与建议
- 捕获精准错误信息:当前代码仅重新抛出异常消息,建议在except块中添加日志记录,输出
LEMSClientAPI.LastError的完整内容(包括错误码、响应内容),同时记录异常的StackTrace,明确是连接失败、认证失败还是API逻辑错误 - 强制指定TLS版本:
- Delphi 10.3.1的HTTP客户端默认依赖Windows SCHANNEL,64位环境下需确保Apache支持TLS 1.2及以上版本
- 显式配置TLS版本:通过
LEMSClientAPI.HTTPClient.SSLOptions.SSLVersions := [TSSLVersion.TLSv1_2, TSSLVersion.TLSv1_3];强制客户端使用高版本TLS,避免版本不匹配
- 验证服务端连通性:
- 用curl或Postman直接调用
AInfo.BaseURL对应的/users接口,携带MasterSecret认证(请求头添加X-EMS-MasterSecret: [你的密钥]),确认服务端可正常响应 - 检查
AInfo.BaseURL是否正确,64位部署后RAD Server的API端点是否有端口、路径变更
- 用curl或Postman直接调用
- 排查64位环境依赖:
- 确认Windows Server已安装Delphi 10.3.1的64位运行时库,以及RAD Server所需的64位组件(如Indy、REST组件)
- 检查Apache反向代理配置:是否正确转发HTTPS请求到RAD Server的64位服务端口,是否存在请求头丢失(如
Host、X-Forwarded-Proto)的情况
- 对比进程内API与远程API差异:
- 代码中同时使用了
TEMSInternalAPI(进程内调用)和TEMSClientAPI(远程调用),尝试用TEMSInternalAPI替代TEMSClientAPI执行QueryUserName操作,若成功则说明问题出在远程调用链路,而非用户逻辑
- 代码中同时使用了
- 验证用户名字符处理:
- 检查
LAgency + '.' + LUserName拼接后的用户名在64位环境下的字符编码是否与32位一致(比如ANSI转UTF-8是否存在问题),可尝试用硬编码的测试用户名调用QueryUserName,排除字符处理导致的错误
- 检查
内容的提问来源于stack exchange,提问作者Ken Davis
相关产品推荐
相关产品推荐

