Gosha ([info]georgick) wrote in [info]tyaps_asp,
@ 2007-07-27 13:17:00
Previous Entry  Add to memories!  Tell a Friend!  Next Entry
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\Версия FrameWork\ - HowTo в MSDN).

Пусть у нас есть два приложения, чья сессия хранится посредством 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 обновила свои значения, а также начните новые сессии на сайте.

У меня всё.



(Post a new comment)


[info]ionflux
2007-07-27 10:26 am UTC (link)
А не проще ли было создать табличку соответствий, и один раз закакать процедурку, а не каждый раз?

У меня по крайней мере через табличку работает отлично.

(Reply to this)(Thread)


[info]georgick
2007-07-27 10:53 am UTC (link)
конечно, это тоже вариант.
я описал простейший способ для двух сайтов.
Табличку выгоднее при достаточно большом количестве таких сайтов.

(Reply to this)(Parent)


Create an Account
Forgot your login?
Login w/ OpenID
English • Español • Deutsch • Русский…