从 MS Access VBA 前端在 SQL 中使用 SESSION_CONTEXT

Posted

技术标签:

【中文标题】从 MS Access VBA 前端在 SQL 中使用 SESSION_CONTEXT【英文标题】:Using SESSION_CONTEXT in SQL from an MS Access VBA frontend 【发布时间】:2019-12-12 01:03:07 【问题描述】:

我正在将 MS Access 后端数据库迁移到 SQL 服务器。

现有的 MS Access 前端需要保留。

我正在使用服务帐户将 Access 前端连接到 SQL 数据库,这样个人用户就无法直接访问 SQL。

我想记录 UserId 的记录添加和更新操作,但我不想在每次调用时都指定字段。

我在 Access 中打开了一个隐藏表,以保持与 SQL 数据库的持久连接。

我使用在 Access 启动时调用的 Sub 在 Access 中创建了一个带有 UserId 的 Session Context 对象,我什至在运行记录插入之前直接调用了 Sub。

Sub SqlSetUser()
Dim qdef As DAO.QueryDef

Set qdef = CurrentDb.CreateQueryDef("")
qdef.Connect = CurrentDb.TableDefs("dbo_User").Connect
qdef.SQL = "EXEC sys.sp_set_session_context @key = N'UserId', @value = '" & GetUser() & "';"
qdef.ReturnsRecords = False  ''avoid 3065 error
qdef.Execute

End Sub

我在 SQL 表中创建了一个触发器来提取 UserId 并将其添加到正在添加的记录中,并使用类似的触发器来处理更新;

CREATE   TRIGGER [dbo].[ReferenceItemAdd]
on [dbo].[ReferenceItem]
FOR INSERT
AS
BEGIN
    SET NOCOUNT ON;
    declare @UserId as int = try_cast((Select SESSION_CONTEXT(N'UserId')) as int)
    UPDATE ReferenceItem set AddDate = getdate(), AddUserId = @UserId
    from INSERTED i, ReferenceItem a
    where i.ReferenceItemId = a.ReferenceItemId
    SET NOCOUNT OFF;
END

只有在我通过断点停止代码然后继续时才有效。如果我允许代码运行,则插入记录,并且触发器正确设置了 AddDate 但 UserId 返回为 NULL。

如何使 MS-SQL 中的触发器可以从 MS-Access 前端访问 UserId?

【问题讨论】:

欢迎来到 SO!请使用tour 并阅读How to Ask(如果尚未完成)。您不使用 Windows 身份验证登录 SQL Server 的任何原因?那会在登录会话时提供用户名吗?安全不是理由,因为用户可以轻松嗅探凭据,而 GetUser 就是这样。像 Environment("username") 是不可靠的。 只是忘了SQL Server Audit 什么可能比触发器更好。 @ComputerVersteher 计划最终转移到 Visual Studio C# 环境,在该环境中,服务帐户凭据将无法轻松获取,并且授予基于服务帐户/应用程序的权限是比授予用户更好的解决方案直接访问 SQL。 @PhillipJones 您是否在连接中设置 MARS? @PhillipJones 我的印象是取消设置 MARS 可能会改善这种情况。看看备注部分:docs.microsoft.com/en-us/sql/t-sql/functions/… 【参考方案1】:

我没听懂您关于此处提高安全性的说明?

如果您有一个客户端 Web 浏览器和一个 Web 服务器,那么您肯定有一个 Web 服务器可以使用服务帐户更新 SQL 数据库,因为您在客户端和 SQL 数据库之间有一个完整的 Web 服务器。

您没有上述任何一项。

例如:

qdef.Connect = CurrentDb.TableDefs("dbo_User").Connect
qdef.SQL = "EXEC 

对,所以你有链接表,上面是一个连接字符串,它直接访问数据库,甚至能够执行存储过程。我假设此连接与链接表使用的连接相同? (无论如何,我们可以将假设的安全问题留到另一天——你这里有的是平面简链接表,它们直接更新数据库,甚至能够按照你上面的例子执行存储过程代码。

接下来:

我们假设这里使用了一个 SQL 登录?

您的代码应该可以工作,但看起来有问题的是:

declare @UserId as int = try_cast((Select SESSION_CONTEXT(N'UserId')) as int)

为什么要将上面的内容转换为 int?是不是GetUser() 你设置了一个字符串? (您的示例代码在文本周围有“ ” - 因此它假定为字符类型。

这是一个直接的变量赋值——你不需要选择。

你应该使用:

 DECLARE @UserID as varchar(25) = CAST(SESSION_CONTEXT(N'UserId') AS varchar(25))

我不知道会话是否对于所有链接表都保持不变。我会 100% 确保所有链接表都具有完全相同的连接字符串。您应该能够在启动时执行一次代码以设置该会话值。但是,我不能 100% 确定这里会始终使用单个会话(您可以回来确认这一点 - 如果是这种情况,我很感兴趣)。

【讨论】:

我知道我现在没有任何 Web 服务器组件,但我计划在不久的将来拥有。一旦我在那里,我将没有任何问题,将服务帐户连接到 SQL 数据库并将 SESSION_CONTEXT 从我的前端传递到数据库。问题是如何使它现在与 Access 一起工作。更改 MARS 设置并在 Access autoexec 中添加持久连接已解决该问题。 get GetUser() 函数从 SQL 数据库中的记录中返回与 UI AD 用户名匹配的 int。

以上是关于从 MS Access VBA 前端在 SQL 中使用 SESSION_CONTEXT的主要内容,如果未能解决你的问题,请参考以下文章

MS Access:使用 VBA 进行 SQL 插入的日期格式

使用 vba 在 MS-Access 前端中来自 MySQL 存储过程的多个结果集?

如何使用sql语句和vba将数据从MS-Access导入excel power查询?

使用 Excel VBA 查询 MS Access,SQL BETWEEN 日期查询

MS Access - 在 VBA 中编写 SQL

如何使用查询或 VBA 和 SQL 更新 MS ACCESS 中的表