Ale*_*xei 4 sql-server connections
我正在开发一个应用程序,该应用程序具有多个严重依赖存储过程的遗留模块(没有 ORM,因此所有获取和数据持久性都是通过存储过程完成的)。
遗留模块的安全性依赖于SUSER_NAME()获取当前用户并应用安全规则。
我将其迁移为使用 ORM(实体框架),并且 SQL 连接器将使用通用用户连接到数据库(SQL Server),因此我必须向许多过程提供当前用户名。
为了避免 .NET 代码发生更改,我想到在建立新连接时以某种方式在上下文中“注入”当前用户:
CREATE TABLE dbo.ConnectionContextInfo
(
ConnectionContextInfoId INT NOT NULL IDENTITY(1, 1) CONSTRAINT PK_ConnectionContextInfo PRIMARY KEY,
Created DATETIME2 NOT NULL CONSTRAINT DF_ConnectionContextInfo DEFAULT(GETDATE()),
SPID INT NOT NULL,
AttributeName VARCHAR(32) NOT NULL,
AttributeValue VARCHAR(250) NULL,
CONSTRAINT UQ_ConnectionContextInfo_Info UNIQUE(SPID, AttributeName)
)
GO
Run Code Online (Sandbox Code Playgroud)
当连接打开(或重用,因为使用连接池)时,将使用以下命令:
exec sp_executesql N'
DELETE FROM dbo.ConnectionContextInfo WHERE SPID = @@SPID AND AttributeName = @UsernameAttribute;
INSERT INTO dbo.ConnectionContextInfo (SPID, AttributeName, AttributeValue) VALUES (@@SPID, @UsernameAttribute, @Username);
',N'@UsernameAttribute nvarchar(8),@Username nvarchar(16)',@UsernameAttribute=N'Username',@Username=N'domain\username'
go
Run Code Online (Sandbox Code Playgroud)
(0 CPU,~15 次读取,<6 毫秒)
标量函数可以轻松获取当前用户:
alter FUNCTION dbo.getCurrentUser()
RETURNS VARCHAR(250)
AS
BEGIN
DECLARE @ret VARCHAR(250) = (SELECT AttributeValue FROM ConnectionContextInfo where SPID = @@SPID AND AttributeName = 'Username')
-- fallback to session current, if no data is found on current SPID (i.e. call outside of the actual application)
RETURN ISNULL(@ret, SUSER_NAME())
END
GO
Run Code Online (Sandbox Code Playgroud)
从数据层的角度来看,这种方法是否有任何注意事项(稳健性、性能等)?
谢谢。
在性能方面,每次打开连接时都会产生DELETE和的开销。INSERT或者,您可以使用内置连接 CONTEXT_INFO 来实现此目的。下面的示例将信息存储在固定长度的 48 字节结构中。
EXEC sp_executesql N'
DECLARE @ContextInfo binary(48);
SET @ContextInfo = CAST(CAST(@UsernameAttribute AS nchar(8)) + CAST(@Username AS nchar(16)) AS binary(48));
',N'@UsernameAttribute nvarchar(8),@Username nvarchar(16)',@UsernameAttribute=N'Username',@Username=N'domain\username'
GO
CREATE FUNCTION dbo.getCurrentUser()
RETURNS VARCHAR(250)
AS
BEGIN
DECLARE
@ContextInfo binary(48) = CONTEXT_INFO()
, @Username nvarchar(16);
SET @Username = RTRIM(CAST(SUBSTRING(@ContextInfo, 17, 32) AS nvarchar(16)));
RETURN ISNULL(@Username, SUSER_NAME());
END
GO
Run Code Online (Sandbox Code Playgroud)
此外,sp_set_session_context和SESSION_CONTEXT()在 SQL Server 2016 和 Azure SQL 数据库中可用。如果您可以的话,这是一种更干净的方法。