简体   繁体   English

如何在Datasnap Server Methods单元中获取经过身份验证的用户名和密码?

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

Datasnap authentication is pretty straightforward once you use the correct parameter names (explained in Delphi Datasnap Server User Authentication ). 一旦使用正确的参数名称(在Delphi Datasnap Server用户身份验证中说明),Datasnap身份验证非常简单。 The next problem is to be able to use those same credentials when using a FireDAC Database Connection. 下一个问题是在使用FireDAC数据库连接时能够使用这些相同的凭据。 The answer seems to be implied in Most efficient way to pass SQL Login credentials to Delphi Datasnap servers? 答案似乎隐含在最有效的方式将SQL登录凭据传递给Delphi Datasnap服务器? although "simply forwarded" doesn't really explain how to accomplish the code. 虽然“简单转发”并没有真正解释如何完成代码。 Further, these credentials should be authenticated as the same credentials used to log on to the Datasnap server. 此外,这些凭据应该作为用于登录Datasnap服务器的相同凭据进行身份验证。 This will prevent impersonation at the database level. 这样可以防止在数据库级别进行模拟。

So far I haven't been able to discover any way to obtain the current Datasnap user credentials programmatically from within the Server Methods unit. 到目前为止,我还没有找到任何方法从Server Methods单元中以编程方式获取当前的Datasnap用户凭据。 For example, in a BeforeConnect event. 例如,在BeforeConnect事件中。 The code I'm working on is a standalone server built with Delphi XE7 using a Session lifecycle. 我正在使用的代码是使用会话生命周期使用Delphi XE7构建的独立服务器。

Here is a description of the events that take place when connecting to a Datasnap server and requesting data: 以下是连接到Datasnap服务器并请求数据时发生的事件的说明:

Let me explain further: 让我进一步解释一下:

By using a few ShowMessage instructions I can trace the flow of Datasnap as I connect and make a data request. 通过使用一些ShowMessage指令,我可以在连接并发出数据请求时跟踪Datasnap的流程。 Running the server under Test allows me to display the contents of the various parameters that accompany the events. 在Test下运行服务器允许我显示事件附带的各种参数的内容。 Once set up, running a client that connects to the server and requests data results in the following: 设置完成后,运行连接到服务器并请求数据的客户端会产生以下结果:

Client Login Button 客户登录按钮

  • Credential Dialogue presented (this is my code, not the property of TSQLConnection that gets it wrong.) 提交了凭证对话(这是我的代码,而不是TSQLConnection的属性,它错了。)
  • Username and Password entered. 输入用户名和密码。
  • Paremeters Placed in TSQLConnection.Params [DSAuthenticationPassword, DSAuthenticationUser] Paremeters放置在TSQLConnection.Params [DSAuthenticationPassword,DSAuthenticationUser]中
  • Set TSQLConnection.Connected := True 设置TSQLConnection.Connected:= True
  • Server event OnUserAuthenticate 服务器事件OnUserAuthenticate

    At this point, the connection is established although nothing else has taken place on the server. 此时,虽然服务器上没有其他任何内容,但已建立连接。 Most importantly, neither the ServerClass nor the ServerMethodsClass have been instantiated. 最重要的是,ServerClass和ServerMethodsClass都没有实例化。 There is no place that I can see to store authentication credentials. 我无法看到存储身份验证凭据的位置。

Client GetData Button 客户端GetData按钮

  • Client opens two client datasets consecutively. 客户端连续打开两个客户端数据集。
  • Server DSServerClass1.Create. 服务器DSServerClass1.Create。 This apparently implies that the 这显然意味着
    ServerTransport recognizes the request as valid and proceeds to ServerTransport将请求识别为有效并继续执行
    create the necessary assets to handle the session. 创建处理会话所需的资产。
  • ServerMethodsUnit3.Create. ServerMethodsUnit3.Create。 Somewhere buried in the DSServerClass there is a GetClass invocation that returns the name of the associated ServerMethodsClass. 隐藏在DSServerClass中的某处有一个GetClass调用,它返回相关ServerMethodsClass的名称。 The named class is instantiated. 命名类被实例化。 Since the ServerMethodsClass ultimately descends from a TDataModule this implies property streaming as well. 由于ServerMethodsClass最终来自TDataModule,这也意味着属性流。
  • OnUserAuthorize (Apparently first dataset) OnUserAuthorize(显然是第一个数据集)
  • OnUserAuthorize (Apparently second dataset) OnUserAuthorize(显然是第二个数据集)

Data is returned to client (At this time the database credentials are hard coded into the TFDConnection ConnectionDefinition just to get it to work.) 数据返回给客户端(此时,数据库凭据被硬编码到TFDConnection ConnectionDefinition中,只是为了让它工作。)

OnUserAuthorize presents Sender parameter with a nil value; OnUserAuthorize使用nil值显示Sender参数; also presented is a TDSAuthorizeEventObject that doesn't contain any references that I have been able to use to find the ServerMethods instance, and, finally, a parameter named Valid, a boolean value used to authorize the user. 还提供了一个TDSAuthorizeEventObject,它不包含我能够用来查找ServerMethods实例的任何引用,最后还有一个名为Valid的参数,一个用于授权用户的布尔值。

Note that TDSAuthorizeEventObject contains a reference to TDSServerMethodUserEventObject that DOES contain the Username along with Roles, Authorized Roles and Denied Roles. 请注意,TDSAuthorizeEventObject包含对TDSServerMethodUserEventObject的引用,该引用包含用户名以及角色,授权角色和拒绝角色。 However, so what? 但是,那又怎样? This brings me back to my original question: How do I communicate this to code in the ServerMethodsUnit? 这让我回到原来的问题: 如何将此信息传达给ServerMethodsUnit中的代码?

After some time I stumbled on the exact methodology to do this in a white paper by Bob Swart. 过了一段时间,我在Bob Swart的白皮书中偶然发现了确切的方法。 There are several methods that will enable you to persist information during a Datasnap session. 有几种方法可以让您在Datasnap会话期间保留信息。 These belong to TDSSessionManager.GetThreadSession and are GetData, PutData, RemoveData, HasData, GetObject, PutObject, RemoveObject and HasObject. 它们属于TDSSessionManager.GetThreadSession,分别是GetData,PutData,RemoveData,HasData,GetObject,PutObject,RemoveObject和HasObject。 These methods actually manage two dictionaries that are part of the Session. 这些方法实际上管理了作为Session一部分的两个字典。 So for example, in the UserAuthentication event you might store the username as follows: 例如,在UserAuthentication事件中,您可以存储用户名,如下所示:

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

Later, in a OnBeforeConnect event for a database connection, you can retrieve these values for use in the connection: 稍后,在用于数据库连接的OnBeforeConnect事件中,您可以检索这些值以在连接中使用:

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

In practice I find the "object" forms of greater value but either form offers a powerful way to persist your own data for the life of a session without resorting to external media. 在实践中,我发现“对象”形式具有更大的价值,但任何一种形式都提供了一种强大的方式,可以在不诉诸外部媒体的情况下在会话期间保留您自己的数据。

In your linked question , the TServerContainer1.DSAuthenticationManager1UserAuthenticate method has User and Password parameters. 在链接的问题中TServerContainer1.DSAuthenticationManager1UserAuthenticate方法具有UserPassword参数。 Your server side code can retrieve the credentials from the request parameters, validate them, and re-use them for authentication with the database. 您的服务器端代码可以从请求参数中检索凭据,验证它们,然后重新使用它们进行数据库身份验证。

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; 

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM