如何在 Datasnap 服务器方法单元中获取经过身份验证的用户名和密码?

How can I obtain the authenticated Username and Password in a Datasnap Server Methods unit?

Datasnap 身份验证非常简单,一旦您使用了正确的参数名称(在 Delphi Datasnap Server User Authentication). The next problem is to be able to use those same credentials when using a FireDAC Database Connection. The answer seems to be implied in Most efficient way to pass SQL Login credentials to Delphi Datasnap servers? 中有解释,尽管 "simply forwarded" 并没有真正解释如何完成代码。此外,这些凭据应该被验证为用于登录 Datasnap 服务器的相同凭据。这将防止在数据库级别进行模拟。

到目前为止,我还没有发现任何方法可以从服务器方法单元中以编程方式获取当前 Datasnap 用户凭据。例如,在 BeforeConnect 事件中。我正在处理的代码是使用会话生命周期使用 Delphi XE7 构建的独立服务器。

以下是连接到 Datasnap 服务器并请求数据时发生的事件的描述:

让我进一步解释:

通过使用一些 ShowMessage 指令,我可以在连接和发出数据请求时跟踪 Datasnap 的流程。 运行 测试中的服务器允许我显示伴随事件的各种参数的内容。设置完成后,运行 连接到服务器并请求数据的客户端会产生以下结果:

客户端登录按钮

客户端获取数据按钮

数据返回给客户端(此时数据库凭证被硬编码到 TFDConnection ConnectionDefinition 中只是为了让它 工作。)

OnUserAuthorize 为 Sender 参数提供一个 nil 值;还提供了一个 TDSAuthorizeEventObject,它不包含我能够用来查找 ServerMethods 实例的任何引用,最后还有一个名为 Valid 的参数,一个用于授权用户的布尔值。

请注意,TDSAuthorizeEventObject 包含对 TDSServerMethodUserEventObject 的引用,它确实包含用户名以及角色、授权角色和拒绝角色。然而,那又怎样?这让我回到了我最初的问题:我如何将它传达给 ServerMethodsUnit 中的代码?

在您链接的 question 中,TServerContainer1.DSAuthenticationManager1UserAuthenticate 方法有 UserPassword 参数。您的服务器端代码可以从请求参数中检索凭据,验证它们,并重新使用它们对数据库进行身份验证。

procedure TServerContainer1.DSAuthenticationManager1UserAuthenticate(
  Sender: TObject; const Protocol, Context, User, Password: string;
  var valid: Boolean; UserRoles: TStrings);

if UserService.isUserValid(User, Password) then
begin
  // use User and Password ... 

end; 

一段时间后,我在 Bob Swart 的白皮书中偶然发现了执行此操作的确切方法。有几种方法可以让您在 Datasnap 会话期间保留信息。这些属于 TDSSessionManager.GetThreadSession,分别是 GetData、PutData、RemoveData、HasData、GetObject、PutObject、RemoveObject 和 HasObject。这些方法实际上管理着作为 Session 一部分的两个字典。因此,例如,在 UserAuthentication 事件中,您可以按如下方式存储用户名:

TDSSessionManager.GetThreadSession.PutData('entrykey', UserName);

稍后,在数据库连接的 OnBeforeConnect 事件中,您可以检索这些值以用于连接:

DBUserName := TDSessionManager.GetThreadSession.GetData('entrykey');

在实践中,我发现 "object" 形式的价值更大,但任何一种形式都提供了一种强大的方式来在会话的生命周期内保留您自己的数据,而无需诉诸外部媒体。