Распределенный сервер баз данных. Как залогинить пользователя?
###############################################
###############################################
###############################################
Ситуация в следующем - в приложении используется удаленный модуль данных(RDM), который содержит компоненты: TDataSetProvaider,TADOStoredProcedure,TADOConnection.
Вопрос в следующем - существует ли стандартный механизм ввода имени пользователя и пароля для TADOConnection (если LoginPromt установлено в true)на стороне клиента или как это можно реализовать не сохраняя пароль?
Сейчас если LoginPromt тру - при попытке клиента приконектится вылетает ошибка соединения (пароль пользователя не верен). А мне необходимо реализовать работу юзеров каждого под своим логином. На клиенте используется TSocketConnection.
Ситуация в следующем - в приложении используется удаленный модуль данных(RDM), который содержит компоненты: TDataSetProvaider,TADOStoredProcedure,TADOConnection.
Вопрос в следующем - существует ли стандартный механизм ввода имени пользователя и пароля для TADOConnection (если LoginPromt установлено в true)на стороне клиента или как это можно реализовать не сохраняя пароль?
Сейчас если LoginPromt тру - при попытке клиента приконектится вылетает ошибка соединения (пароль пользователя не верен). А мне необходимо реализовать работу юзеров каждого под своим логином. На клиенте используется TSocketConnection.
а на сервере точно стоит аутентификация собственными средставми? а не через active directory
а на сервере точно стоит аутентификация собственными средставми? а не через active directory
Да, параметры аутендификации на SQL-servere установлены собственными средствами. Если коннектится к нему на прямую - проблем не возникает. Но по удаленному доступу это не работает.
OleVariant L = (Username+"@"+Password);
T.AS_Login(L);
Естественно необходимо определить метод интерфейса в классе удаленного модуля.
Правда с этими СОМ интерфейсам одно мучение - в связи с тем что раньше работать не приходилось, потому и возникает куча неполадок.
Первое. Странное поведение. В начале в метод передавал 2 параметра. Не работало, кричало, интерфейс неопределен. Сделал так:
T.BindDefoult();
OleVariant L = (Username+"@"+Password);
T.AS_Login(L);
Заработало. Стало работать и в первом варианте Правда юзерский дата сет начал ругатся. Выскакиват экзыпшен "Аргументы имеют неверный тип,выходят за пределы диапазона или конфликтуют друг с другом". Хм.
Соответственно возникает вопрос - че ему надо?В билдеровской справке, такой же метод определен вроде как ОлеВариант, но один. Переделал. Перестало работать все.:) Перезагрузился. Заработало.:)Правда исключение вылетает по прежнему. Буду разбираться... Правда в чем бока понять пока не могу. Читаю СОМ. :)^):)
Вобщем работает это так:
OleVariant L = (Username+"@"+Password);
T.AS_Login(L);
Естественно необходимо определить метод интерфейса в классе удаленного модуля.
Правда с этими СОМ интерфейсам одно мучение - в связи с тем что раньше работать не приходилось, потому и возникает куча неполадок.
Первое. Странное поведение. В начале в метод передавал 2 параметра. Не работало, кричало, интерфейс неопределен. Сделал так:
T.BindDefoult();
OleVariant L = (Username+"@"+Password);
T.AS_Login(L);
Заработало. Стало работать и в первом варианте Правда юзерский дата сет начал ругатся. Выскакиват экзыпшен "Аргументы имеют неверный тип,выходят за пределы диапазона или конфликтуют друг с другом". Хм.
Соответственно возникает вопрос - че ему надо?В билдеровской справке, такой же метод определен вроде как ОлеВариант, но один. Переделал. Перестало работать все.:) Перезагрузился. Заработало.:)Правда исключение вылетает по прежнему. Буду разбираться... Правда в чем бока понять пока не могу. Читаю СОМ. :)^):)
На сервере в TypeLibrary создаешь новый метод, у него два параметра типа BSTR, Refresh
{
try
{
...
//проверить есть ли такой юзнер, установить пареметры набору данных и активировать его
на стороне клиента
svr.OleProcedure("Login", Eduser->Text, Edpassword.text);
//открыть scClient
...
примерно так, код найти не могу похерил, но все работало.
На сервере в TypeLibrary создаешь новый метод, у него два параметра типа BSTR, Refresh
{
try
{
...
//проверить есть ли такой юзнер, установить пареметры набору данных и активировать его
на стороне клиента
svr.OleProcedure("Login", Eduser->Text, Edpassword.text);
//открыть scClient
...
примерно так, код найти не могу похерил, но все работало.
Спасибо. Работает.
да и так тоже работает:
OleCheck(ConOTCRemouteModule::Create(m_remoute));
OleCheck(m_remoute.AS_Login(L));
но мой вариант имеет один не достаток - он инициализирует удаленній сервер дважды. Благодаря твоему коду выяснил как это убрать.
Но проблема и в одном случае и в другом одна и та же. Я почему то не могу обратится к компонентам расположенным в ремотемодуле. Т.е. код на сервере (окна собщений и получение строки имени и пароля - исключительно для проверки):
WideString P = S.SubString(S.Pos("@")+1,S.Length());
WideString U = S.SubString(1,S.Pos("@")-1);
ShowMessage(WideCharToString(P)+" "+WideCharToString(U));
//WideString P = Pass.bstrVal;WideString U = Login.bstrVal;
WideString str = "Provider=SQLOLEDB.1;Password="+P+";Persist Security Info=True;User ID="+U+";Initial Catalog=ncredcom;Data Source=(local)";
ShowMessage (WideCharToString(str));//Вот этот мессага появляется
//А вот эта уж нет. ShowMessage(WideCharToString(nOTCRemouteModule->adoConnect->ConnectionString));
nOTCRemouteModule->adoConnect->ConnectionString = str;
ShowMessage("Strocf");
try{
ShowMessage("Begin");
nOTCRemouteModule->adoConnect->Connected = true;
ShowMessage("End");
nOTCRemouteModule->adoSelect->Active = true;
ShowMessage("Begin");
}
catch(Exception &e){
return Error(e.Message.c_str(),IID_InOTCRemouteModule);
}
return S_OK;
Ошибка оченно информативная - "Ошибка на сервере" :)
Сейчас разбираюсь, возможно проблема связана с неверной инициализацией, или какими-то особенностями удаленного модуля данных. Потому как в стандартном модуле данная конструкция работает без проблем.
Или может быть это связано с потоковой моделью? Для данного сервера модель используется Фри. И необходимо проверять заблокирован объект или нет? тогда чем заблокирован?
Сейчас разбираюсь, возможно проблема связана с неверной инициализацией, или какими-то особенностями удаленного модуля данных. Потому как в стандартном модуле данная конструкция работает без проблем.
Или может быть это связано с потоковой моделью? Для данного сервера модель используется Фри. И необходимо проверять заблокирован объект или нет? тогда чем заблокирован?
Если бы это был набор BDE то рекомендовал -TSession. Попробуй в вставить:
STDMETHODIMP TMyIntefaceImpl::Login(BSTR user, BSTR password)
{
TCOMCriticalSection::Lock Lock(cs);
//обращение к наборам данных
Если бы это был набор BDE то рекомендовал -TSession. Попробуй в вставить:
STDMETHODIMP TMyIntefaceImpl::Login(BSTR user, BSTR password)
{
TCOMCriticalSection::Lock Lock(cs);
//обращение к наборам данных
Нет, пробовал, проблема остается.
Нет, пробовал, проблема остается.
Не знаю - окончательное ли это решение проблемы - но правильный вариант такой:
STDMETHODIMP TnOTCRemouteModuleImpl::AS_Login(VARIANT Login)
{
WideString S = (Login.bstrVal);
WideString P = S.SubString(S.Pos("@")+1,S.Length());
WideString U = S.SubString(1,S.Pos("@")-1);
WideString str = "Provider=SQLOLEDB.1;Password="+P+";Persist Security Info=True;User ID="+U+";Initial Catalog=ncredcom;Data Source=(local)";
Form1->SetConnect(str);
return S_OK;
}
void __fastcall TForm1::SetConnect(const WideString &str){
adoConnect->ConnectionString = str;
adoConnect->Connected = true;
adoSelect->Active = true;
return;
}
То есть, вынесение данных на форму, проблему решило. Остается открытым вопрос с блокировками - честно говоря еще не знаю как поведет себя сервер с несколькими пользователями и надо ли генерировать исключение на стороне сервера в случае например неверного логина - или сервер сгенерет его сам вобщем вопросы навеное еще будут :)
__fastcall TnOTCRemouteModule::TnOTCRemouteModule(TComponent* Owner) : TCRemoteDataModule(Owner)
{
cs = new TCriticalSection;
cs->Acquire();
dmMain = new TdmMain(this);
}
STDMETHODIMP TnOTCRemouteModuleImpl::AS_Login(VARIANT Login)
{
WideString S = (Login.bstrVal);
WideString P = S.SubString(S.Pos("@")+1,S.Length());
WideString U = S.SubString(1,S.Pos("@")-1);
WideString str = "Provider=SQLOLEDB.1;Password="+P+";Persist Security Info=True;User ID="+U+";Initial Catalog=ncredcom;Data Source=(local)";
dmMain->SetConnect(str);
return S_OK;
}
void __fastcall TnOTCRemouteModule::CRemoteDataModuleDestroy(
TObject *Sender)
{
cs->Release();
delete cs;
delete dmMain;
}
Вот такие пирожки с котятами.
Одна из проблем с которыми столкнулся - при закрытии приложения - вылетает ошибка доступа на сервере. Если не вызывать делиты - то проблем вроде нет но отжирается память. :(
Будет еще чего интересного - выложу.
В проекте программа должна работать следующим образом:
1. Вызывается удаленный СОМ-сервер.
2. При успешном вызове - вызывается метод авторизации в который передаются необходимые данные.
3. Процесс авторизации на сервере генерирует события - OnLogin or OnFailure.
4. Событие обрабатывается клиентом - соответственно если все путем - вызывается метод - коннект ДБ в котором создается динамически подключение к базе (создается датамодуль и т.п.), если событие не обработанно, обработанно неправильно или логин неверен - первыйнах.
5. Далее - закачка и передача данных и т.п.
Авторизация происходит не в домене - т.е. механизм виндоус использовать у меня не выходит. Это предистория.
Все работает нормально. События вызываются, клиенты логинятся и в целом все нормально кроме одного. Я не могу связать объект СОМ и внутренний объект SocketConnection. Не получается. Точнее коечто выходит. Т.е используя свойство AppServer - я могу получить доступ к методам сервера. Но я не могу подписать его на нужные мне события! То есть - если я объявляю:
::CreateRemoute("server",com);
TCOMmeComEv events;
events.OnLogin = TgfmMain::OnLogin;
events.Connect(com);
com->LoginEmployee(login,pass,id);
замечательно но не все.
Соответственно если коннетится SocketConnection - он создает новый поток на сервере - и все мои логины ему до задницы :) Правда доступа к базе он тоже не получает. Хоть что то хорошое.
Если я пытаюсь выполнить обратную последовательность - т.е. законнектить компонент, а затем уже вызвать процедуру сервера - получаю ошибку при регистрации событий. Запарился.
Из этого я вижу несколько способов решения:
1. Дописать в копонент свойство, позволяющее подключить его к уже существующему серверу. Вроде самое простое решение и наверное попробую реализовать сегодня ночью.
2. Разобраться как это выполнить не расширяя компонент :) Сюдя по всему - мало реальная и не сбыточная мечта. Так как поля класса закрыты - доступ к ним я могу получить только через хак или в самом классе - первое не годится в связи с ненадежностью, второе - сказанно выше.
3. Использовать механизм транзакций СОМ+. Т.е. создать компонент зависящий от первого - установить его свойство транзакции в "требуется" и стартовать ее из родительского - коннекшион соответственно делать к дочернему.
Вот третий способ мне кажется наиболее оптимальным, но так как мне еще не приходилось реализовать приложения COM+, ну кроме тестового сегодня ночью :), подскажите плиз, правильны ли мои выкладки - и желательно - что необходимо может учесть при этом. Естетвенно, если кто либо чтото подобное делал.
Тестовый сервер у меня стартовал нормально - правда я не успел протестировать работу методов и событий и еще не до конца разобрался с транзакциями в СОМ+. Займусь этим сегодня - если у кого есть любимые грабли - поделитесь :)
Займусь этим сегодня - если у кого есть любимые грабли - поделитесь :)
Ой, давно это было, когда баловался удаленными модулями данных...
Я бы на твоем месте попробовал получить дополнительный, собственный интерфейс от стандартного интерфейса AppServer (или как там он называется)? Кажется, нечто подобное у меня когда-то получилось. Только вот не помню, делалось это ДО подключения пользователя или ПОСЛЕ.
Ой, давно это было, когда баловался удаленными модулями данных...
Я бы на твоем месте попробовал получить дополнительный, собственный интерфейс от стандартного интерфейса AppServer (или как там он называется)? Кажется, нечто подобное у меня когда-то получилось. Только вот не помню, делалось это ДО подключения пользователя или ПОСЛЕ.
Если не затруднит - небольшой пример? Потому как совсем запутался. Вызвать процедуру или функцию через Variant получается - но связать интерфейсы - невыходит хоть ты тресни. Выбивает - как правило с ошибкой доступа.