2014-01-30 2 views
10

Мне поручено написать веб-интерфейс для устаревшей базы данных, где у всех пользователей есть учетные записи баз данных и соответственно назначены роли (у нас есть триггеры по всему месту записи, когда пользователи определенные вещи, все на основе user_name()).Пул олицетворения и подключения SQL Server

Чтобы использовать что-нибудь удаленно современное и чтобы не хранить пароль пользователя в текстовом формате, я подключаюсь к учетной записи уровня App, у которой есть привилегии олицетворения для каждого пользователя, и я пытаюсь запустить Execute As [email protected] и Revert. для установки и сброса контекста выполнения до и после запуска любого SQL.

К сожалению, reset_connection призыва Пулы соединений является отвод ж/моего подключения, и ветров бросали некоторые неприятные ошибки о физической связи является недействительной ...

я могу обойти эту ошибку, не используя пул соединений , Но тогда мой пользователь приложения нуждается в безумном количестве привилегий, чтобы фактически выполнить олицетворение. Кроме того, убийство пулов соединений является обломком ...

Как я могу это сделать без ущерба для безопасности или производительности? Имейте в виду, что я не могу изменить тот факт, что у моих пользователей есть логины базы данных, и я действительно не в восторге от того, что пользовательские пароли восстанавливаются. Является ли мой единственный вариант обхода пула соединений, поэтому я могу выдавать себя за него (и использовать пользователя sa, чтобы у меня было достаточно прав для фактического олицетворения кого-то)?

+0

Примечание. Ошибки физического соединения совпадают со следующей ошибкой: соединение было отброшено, потому что директор, который его открывал, впоследствии принял новый контекст безопасности, а затем попытался сбросить соединение под своим олицетворенным контекстом безопасности. Этот сценарий не поддерживается. См. «Обзор олицетворения» в электронной документации. – Crisfole

+0

Будет ли пользователь веб-приложения подключаться к проверке подлинности Windows и способен ли керберо домена? –

+1

Как насчет [ab] с использованием другой части параметров соединений, например. Имя приложения или идентификатор рабочей станции (http://www.connectionstrings.com/all-sql-server-connection-string-keywords/) в сочетании с функциями 'App_Name()' или 'Host_Name()'? Не идеально, но может быть приемлемым обходным решением, учитывая ограничения ... – gvee

ответ

1

Для реализации своего рода «фальшивого» делегирования без огромных изменений в коде приложения/баз данных я предлагаю использовать context_info() для транспортировки текущего пользователя в базу данных и заменить вызовы user_name() с вызовами dbo.fn_user_name().

Пример того, как создать это решение

Создать функцию 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 

Теперь вы можете заменить все вызовы на имя_пользователя() в коде и заменить их на эту функцию.

Вставить контекст в вашей БД вызывает в .net

Есть 2 варианта здесь. Или вы создаете свой собственный класс SqlConnection или создаете фабричный метод, который возвращает открытое sqlconnection, как показано ниже. У заводского метода есть проблема, что каждый запрошенный вами запрос будет иметь 2 дБ вызовов. Это наименьший код для записи.

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), @user + replicate(N' ', 64 - len(@user))) 
set context_info @a 

, что класс будет использоваться как:

using(var conn = new SqlContextInfoConnection(connectionString, user)) 
{ 
    var cmd = new SqlCommand("select 1", conn); 
    conn.open; 
    return conn.ExecuteScalar() 
} 

Я бы лично реализовать вариант 2, как это было бы ближе к тому, как нормальное SqlConnection работы.

+0

несколько проблем с этим решением: 1. Он несовместим с нашим старым приложением VB6, которое я могу 't изменение и не может использовать вышеуказанный код. 2. Я не большой поклонник выполнения двух отдельных запросов каждый раз, когда я хочу что-то выполнить ... – Crisfole

+0

ваше клиентское серверное приложение не нужно изменять, fn_user_name() выплевывает то же самое, что и user_name(), если контекст не является задавать. –

+0

Вот почему я предлагаю вариант 2, где контекстный вызов добавляется к исполняемой строке sql. Я попробую приготовить класс, когда у меня будет время. –

0

Я знаю, что это старый, однако этот пост является единственным полезным ресурсом, который я нашел, и я решил поделиться своим решением, которое строится на Filip De Vos ответах.

У нас также есть устаревшее приложение VB6, которое использует sp_setapprole (я ценю, что это не совсем соответствует оригинальному сообщению OPs). Наши компоненты .NET, которые используют одну и ту же базу данных (и по существу являются частью платформы приложения), в значительной степени основаны на Linq to SQL.

Установление соответствия для соединения с датаконтекстом оказалось затруднительным, учитывая количество открываний и закрытий соединения.

Мы закончили тем, что использовали простую упаковку, как было предложено выше. Единственными переопределенными методами являются Open() и Close(), в которых установлено и не установлено approle.

Public Class ManagedConnection 
    Inherits Common.DbConnection 

    Private mCookie As Byte() 
    Private mcnConnection As SqlClient.SqlConnection 

    Public Sub New() 
     mcnConnection = New SqlClient.SqlConnection() 
    End Sub 

    Public Sub New(connectionString As String) 
     mcnConnection = New SqlClient.SqlConnection(connectionString) 
    End Sub 

    Public Sub New(connectionString As String, credential As SqlClient.SqlCredential) 
     mcnConnection = New SqlClient.SqlConnection(connectionString, credential) 
    End Sub 

    Public Overrides Property ConnectionString As String 
     Get 
      Return mcnConnection.ConnectionString 
     End Get 
     Set(value As String) 
      mcnConnection.ConnectionString = value 
     End Set 
    End Property 

    Public Overrides ReadOnly Property Database As String 
     Get 
      Return mcnConnection.Database 
     End Get 
    End Property 

    Public Overrides ReadOnly Property DataSource As String 
     Get 
      Return mcnConnection.DataSource 
     End Get 
    End Property 

    Public Overrides ReadOnly Property ServerVersion As String 
     Get 
      Return mcnConnection.ServerVersion 
     End Get 
    End Property 

    Public Overrides ReadOnly Property State As ConnectionState 
     Get 
      Return mcnConnection.State 
     End Get 
    End Property 

    Public Overrides Sub ChangeDatabase(databaseName As String) 
     mcnConnection.ChangeDatabase(databaseName) 
    End Sub 

    Public Overrides Sub Close() 

     Using cm As New SqlClient.SqlCommand("sp_unsetapprole") 
      cm.Connection = mcnConnection 
      cm.CommandType = CommandType.StoredProcedure 
      cm.Parameters.Add("@cookie", SqlDbType.VarBinary, 8000).Value = mCookie 

      cm.ExecuteNonQuery() 
     End Using 

     mcnConnection.Close() 
    End Sub 

    Public Overrides Sub Open() 
     mcnConnection.Open() 

     Using cm As New SqlClient.SqlCommand("sp_setapprole") 
      cm.Connection = mcnConnection 
      cm.CommandType = CommandType.StoredProcedure 
      cm.Parameters.Add("@rolename", SqlDbType.NVarChar, 128).Value = "UID" 
      cm.Parameters.Add("@password", SqlDbType.NVarChar, 128)Value = "PWD" 
      cm.Parameters.Add("@fCreateCookie", SqlDbType.Bit).Value = True 
      cm.Parameters.Add("@cookie", SqlDbType.VarBinary, 8000).Direction = ParameterDirection.InputOutput 
      cm.ExecuteNonQuery() 

      mCookie = cm.Parameters("@cookie").Value 
     End Using 
    End Sub 

    Protected Overrides Function BeginDbTransaction(isolationLevel As IsolationLevel) As DbTransaction 
     Return mcnConnection.BeginTransaction(isolationLevel) 
    End Function 

    Protected Overrides Function CreateDbCommand() As DbCommand 
     Return mcnConnection.CreateCommand() 
    End Function 
End Class 

До:

Using dc As New SystemOptionDataContext(sConnectionString) 
    intOption= dc.GetIntegerValue("SomeDatabaseOption") 
End Using 

После:

Using dc As New SystemOptionDataContext(New ManagedConnection(strConnectionString)) 
    intOption= dc.GetIntegerValue("SomeDatabaseOption") 
End Using 

Надеется, что это помогает другим.

Смежные вопросы