从 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查询?