Передача HTTP-аутентифицированного принципала в другой рабочий поток - PullRequest
1 голос
/ 29 ноября 2010

У нас есть веб-интерфейс на нашем сервере бизнес-уровня.

На некоторых страницах нашего веб-приложения создаются очень длительные задачи (может занимать до 10+ минут). Способ обработки этих запросов выглядит примерно так: -

(в потоке HTTP-запроса)

  • делаем подключение к бизнес-серверу.
  • мы создаем новый поток, чтобы длительный вызов проходил в объекте соединения.
  • Затем HTTP-запрос завершается, передав дескриптор браузеру,
  • браузер периодически опрашивает веб-сервер для получения обновлений о ходе выполнения долгосрочной задачи.

Все запросы к бизнес-серверу аутентифицируются - у главной пользовательской страницы соединения должно быть разрешение на вызов метода на бизнес-сервере.

Этот механизм работает нормально, пока наше веб-приложение работает в классическом режиме. Когда мы работаем в конвейерном режиме, мы получаем ObjectDisposedExceptions при опросе браузера.

System.ObjectDisposedException: Safe handle has been closed
at System.StubHelpers.StubHelpers.SafeHandleC2NHelper(Object pThis, IntPtr CleanupWorkList)
at Microsoft.Win32.Win32Native.GetTokenInformation(SafeTokenHandle TokenHandle, UInt32 TokenInformationClass, SafeLocalAllocHandle TokenInformation, UInt32 TokenInformationLength, ref UInt32 ReturnLength)
at System.Security.Principal.WindowsIdentity.GetTokenInformation(SafeTokenHandle tokenHandle, TokenInformationClass tokenInformationClass, ref UInt32 dwLength)
at System.Security.Principal.WindowsIdentity.get_User()
at System.Security.Principal.WindowsIdentity.GetName()
at System.Security.Principal.WindowsIdentity.get_Name()

проблема заключается в том, что субъект Windows, используемый для установления соединения, удаляется по окончании исходного запроса (что понятно - на самом деле я удивлен, что код сработал вообще!).

Чтобы обойти эту проблему, мне было интересно, можно ли создать дубликат принципала HTTP-запроса и использовать его для создания соединения (и избавиться от него после завершения длительной задачи), или это будет возможно? олицетворять принцип HTTP-запроса в рабочем потоке даже после удаления субъекта?

Обновление

(Мой комментарий к вопросу Алиостада был неверным: тестовая страница потерпела неудачу. Мне удалось запутаться настолько, что я написал свою тестовую страницу, чтобы она не использовала тот же путь кода, что и реальный (ошибочный) код. )

Я написал «обходной путь» для этой проблемы: - Мне повезло узнать, для каких ролей / групп будет запрашиваться логика бизнес-сервера, прежде чем будет сделан вызов бизнес-серверу. Таким образом, мой обходной путь - создать новый общий принципал, основанный на членстве субъекта запроса в этих ролях. Долгосрочная задача запускается с использованием общего принципала.

Я не на 100% доволен этим обходным путем, потому что это очень "хак" - то есть я вижу, что он легко упадет, если какая-то логика выполнила (в высшей степени разумную) проверку проверки подлинности личности принципала .

Так что я все равно буду очень признателен за любую помощь / понимание этого вопроса.

Спасибо

1 Ответ

1 голос
/ 29 ноября 2010

Хорошо, вот мой улов:

Прежде всего, если вы создадите поток, весь контекст безопасности текущего потока будет скопирован в новый поток - по умолчанию.Эта операция тяжелая, но очень необходимая (как вы можете себе представить, большинство вещей не будет работать без нее).В случае, если вам нужно предотвратить это, и вам не нужно копировать контекст, есть способ сделать это, и это было объяснено в C # Рихтера через CLR.К счастью, он поделился этим самым кусочком книги здесь и, в основном, вызывает статический метод для предотвращения контекста flowed:

ExecutionContext.SuppressFlow();

Я не могу думать, что это называетсяв WCF, хотя и с помощью Reflector, я нашел здесь одно использование:

  [SecuritySafeCritical]
    private IAsyncResult BeginGetContext(bool startListening)
    {
        Exception exception;
        do
        {
            exception = null;
            try
            {
                try
                {
                    if (ExecutionContext.IsFlowSuppressed())
                    {
                        return this.listener.BeginGetContext(this.onGetContext, null);
                    }
                    using (ExecutionContext.SuppressFlow())
                    {
                        return this.listener.BeginGetContext(this.onGetContext, null);
                    }
                }
                // .... the rest

Интересно, что это используется в 3 местах, одно из них в SharedHttpTransportManager.

Теперь всеможет показаться, что мы нашли проблему, и это ошибка, но я очень в этом сомневаюсь.

Я догадываюсь, что между процессами происходит переработка процесса, и контекст теряется ,Способ доказать или опровергнуть это состоит в том, чтобы использовать perfmon для регистрации всех повторений процесса и выяснения того, был ли какой-либо из них между ними.

Мое решение в основном - что вам может не понравиться!- просто вставить элемент в очередь (MSMQ или простую очередь базы данных) и сделать так, чтобы служба Windows читала его.Поскольку эта операция настолько важна, Я бы никогда не доверился IIS выполнить ее до финиша.

Надеюсь, это вам пригодится.

...