| Gosha ( @ 2007-07-27 13:17:00 |
| Entry tags: | asp.net, sql server, web, webdev |
Расшаривание сессии между веб-приложениями в ASP.NET
Достаточно известная проблема в ASP.NET - это невозможность расшарить сессию между двумя разными веб-приложениямия. Например, это необходимо cделать, когда сайт имеет http и https версии, и в определенный момент нужен переход на версию с SSL этого сайта, или когда у приложения несколько поддоменов, на каждом из которых разные сайты, но аутентификация одна (крупные порталы)
Существует несколько решений - создавать сайт в физической подпапке другого сайта (но здесь имеются свои незадачи), разработка Custom SQL Provider, решения на основе веб-сервиса и т.д.
В своей практике я использую другой, быстрый и надежный вариант, помогающий решить проблему расшаривания сессии. Поскольку я храню сессию для всех своих приложений на live servers в БД (самый безопасный вариант - просто выставляем mode="SQLServer" в конфиге для sessionState, не зависит от памяти, перезапуска приложения и т.д) - то все, что необходимо было сделать - это разобраться, как сессия хранится в SQL Server. Решение проблемы расшаривания сессии стал хак одной системной SQL процедуры
Не буду вдаваться в подробности, как хранится сессия и как работает весь алгоритм состояния сессии в SQL Server. В этом нетрудно разобраться. Обращу лишь внимание, что сериализованная сессия хранится в таблице ASPStateTempSessions, а названия всех приложений, у которых выставлен mode="SQLServer", в таблице ASPStateTempApplications системной базы TempDB.
Править мы будем процедуру TempGetAppID из базы ASPState (эта база по умолчанию не установлена в SQL Server, нужно запускать скрипт - InstallSqlState.sql из C:\WINDOWS\Microsoft.NET\Framework\Верси
Пусть у нас есть два приложения, чья сессия хранится посредством SQL Server.
Заглянем в таблицу ASPStateTempApplications (AppId,AppName - поля, тут все просто) системной базы TempDB. В этой таблице - список нужных нам приложений. AppId приложения формируется от AppName, а вот AppName - это ни что иное, как идентификатор приложения в метабазе IIS.
Выглядят значения AppName примерно вот так:
/lm/w3svc/1703034896/root
/lm/w3svc/1094001889/root
Итак, нам нужно понять, какое из этих идентификаторов отвечает за каждое из наших приложений.
Можно, конечно, полезть в метабазу IIS специальными средствами, но есть способ проще это узнать.
Идем в IIS, открываем нужный нам сайт, смотрим куда складируются логи. (если не получается, то смотрим в C:\WINDOWS\system32\LogFiles и находим какие из логов дает наш сайт). Название папки файла логов как раз будет выглядеть примерно вот так
1703034896
Очевидно, что теперь /lm/w3svc/1703034896/root и есть идентификатор этого сайта в таблице ASPStateTempApplications. Этот идентификатор будет постоянным, пока вы не удалите веб-сайт в IIS. На основании этого значения процедура TempGetAppID (из базы ASPState. Она создается после прогона InstallSqlState.sql) генерит цифровые уникальные ключи для каждого приложения и дальше использует только их как первичные ключи для работы с сессией для каждого сайта.
(т.е каждому AppId теперь сопоставляется приложение). Эти ключи, как я уже сказал, создает процедура TempGetAppID, на вход которой идет AppName, а на выход возвращается AppId (посредством хеширования) и записываются они в поле AppId таблицы ASPStateTempApplications.
После этой процедуры SQL Server уже не нуждается в AppName, он использует для связывания уникальные ключи AppId (которые, как я уже отметил, генерятся на основе AppName) для каждого приложения.
Это означает, что нам достаточно только подправить эту процедуру TempGetAppID.
Все, что нам нужно сделать - заставить процедуру вернуть одинаковый AppId для двух разных приложений (для двух разных AppName на вход.)
Проще простого.
Пусть http приложение в поле AppName таблицы ASPStateTempApplications хранит
/lm/w3svc/1094001889/root
а https приложению соответствует
/lm/w3svc/1703034896/root
Необходимо сделать следующее:
Если на вход в процедуру TempGetAppID идет /lm/w3svc/1094001889/root или /lm/w3svc/1703034896/root, то выходное значение AppId должно быть одинаковым и постоянным.
Иными словами, - сделать преобразования, чтобы на выход в процедуре для этих двух случаев появлялось одно и то же AppId на основе /lm/w3svc/1094001889/root (т.е http версии)
открываем процедуру TempGetAppID и добавляем туда после блока
SET @appName = LOWER(@appName)
SET @appId = NULL
следующие строчки:
IF RTRIM(@appName) = '/lm/w3svc/1703034896/root'
BEGIN
set @appName = '/lm/w3svc/1094001889/root'
END
что означает:
Если на вход в процедуру идет AppName приложения HTTPS, то присвоить в AppName значение приложения HTTP
Итак, мы заставили SQL Server думать, что в случае этих двух приложений, он работает с одним.
Теперь сессия не будет теряться после перехода из http в https или с сабдомена на другой сабдомен или обратно.
Для корректной работы не забудьте перезапустить IIS (iisreset в cmd), чтобы таблица ASPStateTempApplications обновила свои значения, а также начните новые сессии на сайте.
У меня всё.