Forum Webscript.Ru
Программирование => Perl => Тема начата: Inecs от 08 Июля 2005, 17:47:28
-
Господа, возник вот какой вопрос. Есть таблица (БД - MySql).
Структура её такова:
id (пользователя), name (‘имя пользователя’), pass (‘пароль’).
Как организовать аутентификацию пользователя с помощью CGI::Session. Желательно использую БД, а не файлы.
Если можно, то конкретный пример. За ранее спасибо.
-
Inecs:
Есть таблица
имеется ввиду mysql.user? :)
ох, сейчас начнут вас отправлять в CGI::Session::Tutorial и CGI::Session::CookBook. в лучшем случае. :)
Вот как в "разжеванной документации" (тут на форуме мне как-то написали, что мол в перлдок все расписано очень подробненько и нечего на форумах вопросы задавать):
To store session data in MySQL database, you first need to create a suitable table for it with the following command:
CREATE TABLE sessions (
id CHAR(32) NOT NULL UNIQUE,
a_session TEXT NOT NULL
);
You can also add any number of additional columns to the table, but the above "id\'\' and "a_session\'\' are required.
На этих подробностях я пока отложила решение вопроса авторизации. Но идея была таковой:
некто вводит в форму свои логин/пароль, соединяется с базой,
сохраняем сессию на диск и прописываем в таблицу sessions ИД пользователя и номер его сессии, а по ссылкам потом таскаем этот номер сессии или в кваери_стринг или в инпут тайп=хидден.
только не очень понятен механизм, удаления сессии, если например, пользователь поменял пароль. или вообще, что-либо поменял, при очередном входе.
интересный вы вопрос подняли. :)
-
Inecs:
id (пользователя), name (‘имя пользователя’), pass (‘пароль’). Как организовать аутентификацию пользователя с помощью CGI::Session. Желательно использую БД, а не файлы.
отдельно использовать таблицу с данными CGI::Session.
После проверки сесии получаешь id пользователя (+любые другие данные сесии) и соответственно вытягиваешь все из своей БД.
Не стоит одно с другим накрепко связывать.
AnnA:
ох, сейчас начнут вас отправлять в CGI::Session::Tutorial и CGI::Session::CookBook.
именно так :) там все расписано.
-
Green Kakadu да нет там нифига. :) извините.
я говорю именно про алгоритм - реализация не нужна: справлюсь.
Распишите пож-ста, если не сложно.
как я уже говорила - вижу это так:
1. зашел пользователь, подсоединился т.е. к базе (причем напрямую - из формы, а не из восстановленного по номеру сессии паролю. Это важно для случая, если пользователь поменял пароль).
2. считываем из базы номер сессии для этого пользователя
3. востанавливаем параметры сессии по номеру с винта.
(их может быть много.. например... ну не знаю... например кол-во записей в базе, что бы показать сколько их добавилось или удалилось с последнего его визита).
4. сверяем с теми, что он передал в форме (например новый пароль: в базу он зашел с ним, а в сессии прописан еще старый, или язык веб-интерфейса)
4а. если что-либо изменилось - удаляем текущую сессию, удаляем ее номер из базы, происываем новую на винт и запись о ней в базу - отдаем ему его интефейс
4б. ничего не изменилось - отдаем ему его интерфейс.
--------------------
ну. вот так вот. слишком долго, ресурсоемко. нет?
а, если у меня в базе 50 пользователей, я открываю таблицу сессионс и что, - для КАЖДОГО пользователя в цикле давать права селект, делит, инсерт, апдейт? Или можно как-то сделать это сразу для всех?
Green Kakadu:
и соответственно вытягиваешь все из своей БД.
вы предлагаете все хранить в базе?! гм. тогда зачем мне вообще нужен модуль цги::сессион? ключ я и так могу сгенерить, это не проблема. нет. на мой взгляд - надо хранить именно в сессии. с ключем айпи-матч и еще какой-либо своей "чексамм". т.е. даже если узнают номер сессии (что не так уж и сложно) то пришлось бы постараться дабы пройти проверки в ней установленные.
ух. ;) вон сколько написала
-
AnnA:
вы предлагаете все хранить в базе?! гм. тогда зачем мне вообще нужен модуль цги::сессион? ключ я и так могу сгенерить, это не проблема. нет. на мой взгляд - надо хранить именно в сессии. с ключем айпи-матч и еще какой-либо своей "чексамм". т.е. даже если узнают номер сессии (что не так уж и сложно) то пришлось бы постараться дабы пройти проверки в ней установленные.
там и так все хранится в базе. Причем это временное хранилище. Вы для сессии созраняете некоторые нужные вам данные, они сериализуются (Dump/Storable) и сохраняются.
А для того, чтоб не было проблем при смене пароля вы должны пользовательские данные (пароль, имя, настройки и т.д.) хранить в отдельном месте ,а не кидать их в таблицу где сохраняются сессии. К тому же сессии полезно подтирать с некоторой периодичностью.
Алгоритм примерно таков:
зашел пользователь, получили его sid, если такая есть, то вы знаете его id и можете из своих таблиц выбрать то, что нужно (или взять это из данных сессии).
Если sid не существует или устарела, то соответственно, он проходит процесс авторизации и вы создаете ему сессию.
Если он меняет какие-либо данные, вы вносите их (туда где хранятся данные пользователей, а не в сессию), удаляете старую сессию, открываете новую. Сессионные данные ВРЕМЕННЫЕ не стоит туда кидать что-то предназначенное для длительного хранения.
примерно так.
-
Green Kakadu или мы о разном толкуем, или я вас не понимаю.
Green Kakadu:
там и так все хранится в базе.
да нет же. в базе храняится только пользовательский ИД и номер его сессии.
Green Kakadu:
А для того, чтоб не было проблем при смене пароля вы должны пользовательские данные (пароль, имя, настройки и т.д.) хранить в отдельном месте ,а не кидать их в таблицу где сохраняются сессии
нутк в таблице я и храню ТОЛЬКО номер сессии и ничего более.
Green Kakadu:
Если он меняет какие-либо данные, вы вносите их (туда где хранятся данные пользователей, а не в сессию), удаляете старую сессию, открываете новую.
Ну блин. А зачем мне тогда она вообще нужна эта сессия, если в ней только то и есть, что номерок? Да вы не злитесь, просто не могу понять зачем он мне вообще нужен - номер сессии, если все данные хранятся в БД? Тогда я могу "таскать за собой" по страницам, например ИД пользователя +1024 (например. причем число после + может меняться. это несложно реализовать). м?
Green Kakadu:
Сессионные данные ВРЕМЕННЫЕ не стоит туда кидать что-то предназначенное для длительного хранения.
"Временные" - имеется ввиду $session->expire(\'+1h\');? в ЛЮБОМ случае я получаю файл вида: cgisess_3d4799f72a9f3a20e8bf955687afabe3 вот из него я и пишу в базу ТОЛЬКО sid -> 3d4799f72a9f3a20e8bf955687afabe3 т.е.
$dbh->do(qq{ insert into SESSIONS values ($user_id, $sid) }) or die $DBI::errstr;
$dbh->commit or die $DBI::errstr;
-
AnnA
"Временные" - имеется ввиду $session->expire(\'+1h\');? в ЛЮБОМ случае я получаю файл вида: cgisess_3d4799f72a9f3a20e8bf955687afabe3 вот из него я и пишу в базу ТОЛЬКО sid -> 3d4799f72a9f3a20e8bf955687afabe3 т.е.
$dbh->do(qq{ insert into SESSIONS values ($user_id, $sid) }) or die $DBI::errstr;
$dbh->commit or die $DBI::errstr;
мда...
"не понимание сути природы..."
зачем использовать файлы и при всем при этом ещё и БД чего то пихать?
Тогда я могу "таскать за собой" по страницам, например ИД пользователя +1024 (например. причем число после + может меняться. это несложно реализовать). м?
ну да... полностью согласен!
вот только маленькое но! если этих параметров с десяток? их тоже будешь с собой по страницам тоскать?
-
По порядочку. Причем файлы если данные сессии хранятся в БД?
По умолчанию, CGI::Session использует для хранилища файлы, но никто не мешает использовать
БД:
Создаем для этого таблицу:
CREATE TABLE my_sessions (
id CHAR(32) NOT NULL UNIQUE,
a_session TEXT NOT NULL
);
Что тут хранится?
id - уникальный ключ (32 байта) Сгенерированный MD5, SHA1 или способом дедушки на кухне
(модуль Session::ID::Incr)
a_session - это не ид пользователя ,а сериализованные данные (хеш) в котором хранятся время
жизни, ip (если есть) и т.д. + ПО желанию вы можете внести туда что-то свое через метод
param. Сериализуется все по умолчанию через Data::Dumper (Storable побыстрее, так что если
установлен, то возможно стоит его использовать, задается при создании сессии)
Таблица есть, теперь делаем такие движения:
use CGI::Session;
use CGI qw/:cgi/;
my $cgi = new CGI;
#по умолчанию таблица называется sessions
# если у вас не так, то надо указать имя таблицы:
$CGI::Session::MySQL::TABLE_NAME = \'my_sessions\';
# далее вызываем объект сессии. Если она есть и ее срок не истек, то сответственно получаете
#параметры ессии, если истек или ее нет, то автоматом открывается новая
# Нам он НЕ очень нужен :)
my $s = new CGI::Session("driver:MySQL", $cgi, {Handle=>$dbh});
#Используем это:
#Альтернативный способ "без автоматов" сойдет для авторизации - метод load
my $s = CGI::Session->load("driver:MySQL", $cgi, {Handle=>$dbh});
#проверяем срок годности:
if ( $s->is_expired ) {
#если устарела
$s->delete();#удаляем ее, чтоб не засорять
&login;#пусть логинится
exit(0);
}
if ( $s->is_empty ) {
# если ее вообще нет, то пусть тоже логинится
&login;#пусть логинится
exit(0);
}
# все кривые ушли на авторизацию, остались живые
# можем получить нужные параметры
my $user_id = $s->param(\'user_id\');#если он конечно нужен
#зададим какой-нибудь параметр:
$s->param(\'my_param\',\'Хоу ду ю ду миссис Джейн?\');
#делаем что угодно, хотя это нам на самом деле и не нужно
# НО нужно продлить время жизни
$s->expire(\'+2h\');#продлеваем время жизни сессии на пару часов
# время жизни сохраняется и в БД, не только в куках
# куки нам вообще неинтересны
#ля-ля-ля
# Только когда будете отдавать пользователю страницу, не забудьте вначале послать заголовки
print $s->header();
При авторизации, вы проверяете пароль, логин и если все прошло успешно, создаете новую
сессию
my $s = new CGI::Session("driver:MySQL", $cgi, {Handle=>$dbh});
Примерно так.
В куках передается только sid и ничего более, все остальное хранится на стороне сервера.
И хранить там из своего по-моему надо только id пользователя, чтоб его опознать и получить
по этому id данные которые хранятся уже в ваших хранилищах (не важно где)
Можно на несколько разных скриптов использовать одну таблицу сессий ,просто разные скрипты
будет искать данные своих пользователях в разных местах.
CGI::Session - это не инструмент для сохранения пользовательских данных, это лишь инструмент
для временного (в течении сеанса или нескольких сеансов) сохранения временных данных с
привязкой к конкретному пользователю.
Лично мне CGI::Session не понравился по нескольким причинам, если говорить о БД:
универсальность хранения (или в файлах или в БД как хотите) делает его хромым при работе с
БД. Вместо простых SQL запросов для проверки времени истечения жизни сессии, он выбирает
конкретную запись ,восстанавливает сериализованные данные и потом смотрит. Намного лучше,
если бы это значение было в отдельном поле, тогда можно было бы пачкой удалять все
устаревшие сессии, а иначе происходит накопление "мертвых" сессий (один раз вас юзер посетил
, вы ему сделали сессию, и он никогда не вернулся, а в БД запись висит).
Поэтому лучше написать и использовать что-то свое. В случае файлов это не так критично, хотя
тоже захламляется.
Сессии нужно подтирать - это как стикер, написал, повесил на холодильник, через день
прочитал и выкинул. Если понадобилось - взял новую бумажку и там опять накалякал.
P.S. Сорри, если сумбурно, но на развернутуе сочинение how-to пока времени не хватает ,может
соберусь как нибудь..
-
Green Kakadu
Лично мне CGI::Session не понравился
как это знакомо... :) я вообще от готовый решений отказался... свой написал...
-
commander:
если этих параметров с десяток?
да хоть с двадцаток. речь шла о том, что ВСЕ параметры хранятся в БАЗЕ. а по страницам передавать только "например ИД пользователя +1024". зачем писать "хоть что-то", если нечего добавить по сути вопроса. непонятно. именно, что
commander:
"не понимание сути природы..."
Green Kakadu respect. "Разжевали, как ребёнку". :super: :chmok:
-
AnnA
что ВСЕ параметры хранятся в БАЗЕ. а по страницам передавать только "например ИД пользователя +1024"
а чем простите тогда у вашего метода принцепиальное отличие от сессий? :)
зачем писать "хоть что-то", если нечего добавить по сути вопроса.
по сути вопроса мне есть что добавить... по этому и пишу... вы в ввиду, видимо своей некомпетентности, пытаетесь изобрести велосипед да ещё и с квадратными колёсами... а я пытаюсь вас предостеречь от этого... не более того...
to moderators:
sorry за флэйм....
-
[moderator]
Я внимательно смотрю за этой веткой.
Постарайтесь держать себя в рамках.
-
AnnA:
да хоть с двадцаток. речь шла о том, что ВСЕ параметры хранятся в БАЗЕ. а по страницам передавать только "например ИД пользователя +1024".
ничем. поэтому вы сами можете написать нечто ,что будет работать лучше. Передается только sid, он хранится в куках (а можно и по ссылкам таскать)
У CGI::Session просто название такое хорошее, тогда как стандартным модулем для работы с сессиями он не является :)
-
дык а тут в общем-то теперь все понятненько. может и не всем, конечно. ;)
Green Kakadu а можно узнать ваше мнение/отношение об/к Apache::Session::MySQL?
насколько я поняла при беглом чтении документации - принцип приблизительно одинаков и для авторизации действительно
Green Kakadu:
лучше написать и использовать что-то свое
commander извините - у меня нет желания с вами препираться, а по сути вопроса вы так ничего и не сказали. :) спасибо большое за предостережения, но я, в общем-то пытаюсь лишь понять принцип/алгоритм, а вовсе не создавать велосипед.
-
AnnA:
Green Kakadu а можно узнать ваше мнение/отношение об/к Apache::Session::MySQL?
ничего сказать не могу, не использовал :(
если будете выбирать механизм для поддержки сессий то прежде всего посмотрите на воз-ть очистки (массовой) мертвых сессий, срок которых истек. По идее это один sql запрос на удаление всех сессий у которых поле "срока годности" истекло.
-
Посмотрел документацию Apache::Session::Store::MySQL
в общем-то хранение ничем не отличается, тот же sid + хеш с данными в a_session
Для построения какого-нибудь частопосещаемого ресурса (например форума) ИМХО не лучший вариант, потому как сериализация данных - ненужное и лишнее телодвижение, когда обычно от сессии требуется воз-ть идентифицировать пользователя ну и узнать служебные параметры сессии (ip ,время), что лучше бы просто вынести в отдельные поля
-
Green Kakadu спасибо.
-
Простите пожалуйста что вмешиваюсь в вашу беседу,но меня тоже очень интересует принцип использования сессий (в моем случае для авторизации/аутентификации на сайте) с помощью модуля CGI::Session. А проблема моя заклчается в том, что я, даже после прочтения здаровенного мануала(туториала), так и не понял, каким образом можно/нужно проверять является ли пользователь "хозяином" сессии? (т.е. тем,кто ее изначально создал в том виде и с тем ключом, с которым она есть).
А я проделываю следующее:
my $cgi = new CGI;
my $cookie;
my $sid = $cgi->cookie("CGISESSID") || undef;
my $session = new CGI::Session(undef, $sid, {Directory=>\'Z:/tmp\'});
unless ($sid) {
$cookie = $cgi->cookie(CGISESSID => $session->id);
print $cgi->header(-cookie=>$cookie);
}
Это в основном вырезки из туториала. Но вот мне не совсем понятно каким образом происходит АУТЕНТИФИКАЦИЯ. Тоесть определения честности пользователя. И что происходит, когда в качестве второго параметра в конструктор CGI::Session передается значение, не равное undef? Тоесть если undef, насколько я понял, создается новая сессия, а если нет? Есть ли тогда вообще смысл в этой часте кода при $sid не undef? (... new CGI::Session(undef, $sid, {Directory=>\'Z:/tmp\'});
Заранее спасибо за возможные ответы или критику или даже игнор :)
-
Я вот впринципе подумал и мне на ум приходит только один вариант решения данной проблемы. Вот примерный алгоритм:
1. Юзер вводит логин и пароль, нажимает ок.
2. Скрипт проверяет пользователя по базе, если все ок, пользователь существует и пароль верен, то отправляем на главную страницу некого интерфейса.
3. На главной странице создается сессия выше указанным способом(если кукиса нет,то создаем, если есть,пользуемся текущей).
4. Далее, допустим,при перемещении в какой-либо секретный раздел(тоесть раздел защищенного интерфейса), то мы делаем следующее:
4.1 Проверяем пользователя кукисы. Записываем кукис с именем CGISESSID в некую переменную (если он есть). Эта часть кода у меня так выглядит:
my $sid = $cgi->cookie("CGISESSID") || undef;
if ($sid) {
my $session = new CGI::Session(undef, $cgi, {Directory=>\'Z:/tmp\'});
print "Okey!
";
print "Session->ID = ", $session->id(), "
;
} else {
print "nefiga :(
";
}
Вот и все. Но мне почему-то кажеться что существует какой-то другой, более элегантный способ проверки.
-
DSA:
Простите пожалуйста что вмешиваюсь в вашу беседу,но меня тоже очень интересует принцип использования сессий (в моем случае для авторизации/аутентификации на сайте) с помощью модуля CGI::Session. А проблема моя заклчается в том, что я, даже после прочтения здаровенного мануала(туториала), так и не понял, каким образом можно/нужно проверять является ли пользователь "хозяином" сессии? (т.е. тем,кто ее изначально создал в том виде и с тем ключом, с которым она есть).
почитай то что я подробно написал в этом топике выше (с прмером):
http://forums.webscript.ru/showthread.php?s=&postid=129870#post129870
Как определяется пользователь владелец сессии? По sid - ид-ру сессии
DSA:
Вот и все. Но мне почему-то кажеться что существует какой-то другой, более элегантный способ проверки.
да, потому как все это делает CGI::Session самостоятельно (метода load , new)
В том числе и отправку куков, модуль делает сам $session->header
почитайте мой пост выше я там подробно расписал это
-
Большое спасибо за ответ, Зеленый Какаду! :-)
Мне очень понравился ваш развернутый, подробный ответ, но я почему-то не нашел в перловой документации ничего про метод load, да и при попытке использовать его в скрипте перл ругается:
Can\'t locate auto/CGI/Session/load.al in @INC (@INC contains: z:/usr/lib z:/usr/
site/lib .) at xxx.pl line 7
Не могли бы ли вы прислать его мне на мыло mailbest@ru.ru либо скинуть прямо сюда в виде открытого кода? (я думаю он будет не большой по содержанию), я был бы очень признателен.
Кстати, вы писали, что вас лично модуль CGI::Session не устраивает, т.к. "Вместо простых SQL запросов для проверки времени истечения жизни сессии, он выбирает конкретную запись ,восстанавливает сериализованные данные и потом смотрит." - а нельзя ли написать собственную функцию отчистки базы от "мертвых" сессий? Тоесть срок действия которых давно истек.
-
DSA:
ничего про метод load, да и при попытке использовать его в скрипте перл ругается:
Can\'t locate auto/CGI/Session/load.al in @INC (@INC contains: z:/usr/lib z:/usr/ site/lib .) at xxx.pl line 7
сорри :) не сказал, что я смотрел уже новую версию (она еще в процессе разработки):
http://search.cpan.org/~sherzodr/CGI-Session-4.00_08/
DSA:
Кстати, вы писали, что вас лично модуль CGI::Session не устраивает, т.к. "Вместо простых SQL запросов для проверки времени истечения жизни сессии, он выбирает конкретную запись ,восстанавливает сериализованные данные и потом смотрит." - а нельзя ли написать собственную функцию отчистки базы от "мертвых" сессий? Тоесть срок действия которых давно истек.
можно конечно, только это будет мягко говоря "не опримальный" вариант:
т.к. дата истечения хранится в хеше, который в свою очередь через Dump/Storable превращен в строку текста, то придется вытаскивать данные a_session для каждой сессии,восстанавливать эти данные, проверять время истечения и потом уже если оно истекло удалять.
Самый лучший вариант вынести это в отдельное поле (т.е. переписать некоторые методы CGI::Session::Driver::mysql + добавить парочку своих для массовой проверки/удаления), но ИМХО проще тогда написать свой вариант.
-
Green Kakadu:
Самый лучший вариант вынести это в отдельное поле (т.е. переписать некоторые методы CGI::Session::Driver::mysql + добавить парочку своих для массовой проверки/удаления), но ИМХО проще тогда написать свой вариант.
оказывается это уже сделали (правда для PostgreSQL):
CGI::Session::PureSQL (http://search.cpan.org/~markstos/CGI-Session-PureSQL-0.54/)
Там используется таблица вида:
-- This syntax for for Postgres; flavor to taste
CREATE TABLE sessions (
session_id CHAR(32) NOT NULL,
remote_addr inet,
creation_time timestamp,
last_access_time timestamp,
duration interval
);
-
Вау! Пасибки за ценнейшую информацию :) Пойду прямо сейчас обновлять свою 3-95 на 4 версию CGI::Session :)