为了使用远程现代的东西,并且避免将用户的密码存储在纯文本中,我正在连接一个具有每个用户的模拟权限的App级别的帐户,我正在尝试运行Execute As User = @ username恢复在运行任何sql之前和之后设置和重置执行上下文.
不幸的是,连接池的reset_connection调用正在与我的Connection连接,并且它卷起了一些关于物理连接无效的令人讨厌的错误…
我可以通过不使用连接池来解决这个错误.但是,我的应用程序用户需要一个疯狂的权限来实际执行模拟.此外,杀死连接池是一个很大的…
如何在不牺牲安全性或性能的情况下执行此操作?记住,我不能改变我的用户有数据库登录的事实,我真的不兴趣以可检索的方式存储用户密码.我唯一的选择是绕过连接池,所以我可以模仿(并使用sa用户,所以我有足够的权限来实际模拟某人)?
解决方法
关于如何建立此解决方案的示例
创建fn_user_name()函数
我将创建一个函数fn_user_name,它将从连接上的context_info()中提取用户名,或者在没有上下文信息可用的情况下返回user_name().请注意,连接上下文是一个128字节的二进制文件.你放在那里的任何东西都会用零字符填充,为了解决这个问题,我用空格填充值.
create function dbo.fn_user_name() returns sysname as begin declare @user sysname = rtrim(convert(nvarchar(64),context_info())) if @user is null return user_name() return @user end go
现在,您可以在代码中找到替换为user_name()的所有调用,并将其替换为此函数.
这里有2个选项.或者您创建自己的sqlConnection类,或者创建一个工厂方法,它将返回一个打开的sqlconnection,如下所示.工厂方法的问题是,您运行的每个查询都将是2个db调用.这是写最少的代码.
public sqlConnection CreateConnection(string connectionString,string user) { var conn = new sqlConnection(connectionString); using (var cmd = new sqlCommand( @"declare @a varbinary(128) = convert(varbinary(128),@user + replicate(N' ',64 - len(@user))) set context_info @a",conn)) { cmd.Parameters.Add("@user",sqlDbType.NVarChar,64).Value = user; conn.Open(); cmd.ExecuteNonQuery(); } return conn; }
你会用这个:
using(var conn = CreateConnection(connectionString,user)) { var cmd = new sqlCommand("select 1",conn); return conn.ExecuteScalar() }
对于sqlConnection的替代版本,您将需要重载DbConnection并实现sqlConnection的所有方法.执行方法将在下面添加查询,并将用户名作为附加参数传入.
declare @a varbinary(128) = convert(varbinary(128),64 - len(@user))) set context_info @a
那个类将被用作:
using(var conn = new sqlContextInfoConnection(connectionString,conn); conn.open; return conn.ExecuteScalar() }
我会亲自实现选项2,因为它将更接近正常的sqlConnection的工作方式.