Wif-защищенный сервис WCF, кэширующие токены - метод async wcf теряет идентичность - PullRequest
3 голосов
/ 28 апреля 2011

У меня есть служба wcf, которая защищена с помощью WIF. Я реализую кеширование токенов на клиенте (веб-сайте), как описано в блоге Трэвиса Спенсера:

http://travisspencer.com/blog/2009/03/caching-tokens-to-avoid-calls.html

Веб-сайт использует олицетворение (олицетворение меня), и конечная точка STS wcf настроена для использования проверки подлинности Windows.

При вызове службы WCF с использованием прямых вызовов (т. Е. Не асинхронных вызовов) кэширование токенов работает нормально - поведение ClientCredentials удаляется и добавляется мое пользовательское поведение CacheClientCredentials, а токен кэшируется при первом вызове и повторно используется при последующем вызов.

Однако у меня есть сценарий, в котором асинхронный метод вызывается в службе wcf с предоставлением обратного вызова. При нормальных обстоятельствах (не асинхронных) метод CacheClientCredentialsSecurityTokenManager.CreateSecurityTokenProvider вызывается несколько раз, и при каждом вызове идентификатора поток выполняется как правильный. Последующий вызов STS использует правильные учетные данные, пользователь проходит проверку подлинности и токен возвращается. Когда вызывается асинхронный метод, происходят множественные вызовы CacheClientCredentialsSecurityTokenManager.CreateSecurityTokenProvider, но только первый вызов имеет правильную идентификационную информацию. Последующие вызовы имеют «NT AUTHORITY \ NETWORK SERVICE» в качестве идентификатора. В результате вызов к STS имеет неправильные учетные данные, и аутентификация не проходит. (Журнал STS показывает сообщение, указывающее «не удалось аутентифицироваться».

Я пытался добавить явное олицетворение вокруг асинхронных методов Begin / End, но это не всегда работало. Затем я добавил следующие значения в файл web.config:

    <legacyImpersonationPolicy enabled="false" />
    <alwaysFlowImpersonationPolicy enabled="true" />

Это также не всегда работает (хотя иногда работает). Здесь необычно то, что бизнес-логика включает в себя 3 попытки вызвать асинхронный метод (если вызовы не удаются). Я обнаружил, что, как правило, первые 2 завершаются неудачно, а третий завершается успешно - то есть CacheClientCredentialsSecurityTokenManager.CreateSecurityTokenProvider обычно выполняется как неправильный идентификатор первые два раза, а правильный идентификатор - в третий раз, но это также кажется немного случайным. Когда CacheClientCredentialsSecurityTokenManager.CreateSecurityTokenProvider успешно выполняется с правильным идентификатором, вызов WCF завершается успешно. Однако токен не добавляется в кеш (код кеша не выполняется). Последующие вызовы не асинхронного метода затем создают новый кеш, а затем получают токен, добавляя его в кеш.

Каков правильный метод для вызова асинхронного метода WCF, который защищен с помощью WIF, чтобы обеспечить кэширование токена WIF между вызовами?

Требуется ли специальная конфигурация идентификатора, чтобы гарантировать, что один и тот же идентификатор используется во всех этих процессах? (все области используют олицетворение)

Обновление:

Я не уверен, что он добавляет много, но я обнаружил, что когда код не работает, в CacheClientCredentialsSecurityTokenManager используется следующая трассировка стека:

Unflagged   >   5732    18  Worker Thread   <No Name>   CacheClientCredentialsSecurityTokenManager.CreateSecurityTokenProvider  Normal
                        MySecurity.dll!CacheClientCredentialsSecurityTokenManager.CreateSecurityTokenProvider(System.IdentityModel.Selectors.SecurityTokenRequirement tokenRequirement) Line 18  
                        System.ServiceModel.dll!System.ServiceModel.Security.SecurityProtocol.AddSupportingTokenProviders(System.ServiceModel.Security.Tokens.SupportingTokenParameters supportingTokenParameters, bool isOptional, System.Collections.Generic.IList<System.ServiceModel.Security.SupportingTokenProviderSpecification> providerSpecList) + 0xca bytes   
                        System.ServiceModel.dll!System.ServiceModel.Security.SecurityProtocol.OnOpen(System.TimeSpan timeout) + 0xa7 bytes   
                        System.ServiceModel.dll!System.ServiceModel.Security.SymmetricSecurityProtocol.OnOpen(System.TimeSpan timeout) + 0x45 bytes  
                        System.ServiceModel.dll!System.ServiceModel.Security.OperationWithTimeoutAsyncResult.OnScheduled(object state) + 0x82 bytes  
                        System.ServiceModel.dll!System.ServiceModel.Channels.IOThreadScheduler.CriticalHelper.WorkItem.Invoke2() + 0x46 bytes    
                        System.ServiceModel.dll!System.ServiceModel.Channels.IOThreadScheduler.CriticalHelper.WorkItem.OnSecurityContextCallback(object o) + 0x28 bytes  
                        mscorlib.dll!System.Security.SecurityContext.Run(System.Security.SecurityContext securityContext, System.Threading.ContextCallback callback, object state) + 0x55 bytes  
                        System.ServiceModel.dll!System.ServiceModel.Channels.IOThreadScheduler.CriticalHelper.WorkItem.Invoke() + 0x4d bytes     
                        System.ServiceModel.dll!System.ServiceModel.Channels.IOThreadScheduler.CriticalHelper.ProcessCallbacks() + 0x180 bytes   
                        System.ServiceModel.dll!System.ServiceModel.Channels.IOThreadScheduler.CriticalHelper.CompletionCallback(object state) + 0x7a bytes  
                        System.ServiceModel.dll!System.ServiceModel.Channels.IOThreadScheduler.CriticalHelper.ScheduledOverlapped.IOCallback(uint errorCode, uint numBytes, System.Threading.NativeOverlapped* nativeOverlapped) + 0xf bytes     
                        SMDiagnostics.dll!System.ServiceModel.Diagnostics.Utility.IOCompletionThunk.UnhandledExceptionFrame(uint error, uint bytesRead, System.Threading.NativeOverlapped* nativeOverlapped) + 0x3d bytes    
                        mscorlib.dll!System.Threading._IOCompletionCallback.PerformIOCompletionCallback(uint errorCode, uint numBytes, System.Threading.NativeOverlapped* pOVERLAP) + 0x54 bytes     
                        [Appdomain Transition]  

Когда он работает, он немного отличается (обратите внимание на дополнительные записи mscorlib и переходы в середине):

Unflagged   >   5408    12  Worker Thread   <No Name>   CacheClientCredentialsSecurityTokenManager.CreateSecurityTokenProvider  Normal
                        MySecurity.dll!CacheClientCredentialsSecurityTokenManager.CreateSecurityTokenProvider(System.IdentityModel.Selectors.SecurityTokenRequirement tokenRequirement) Line 18  
                        System.ServiceModel.dll!System.ServiceModel.Security.SecurityProtocol.AddSupportingTokenProviders(System.ServiceModel.Security.Tokens.SupportingTokenParameters supportingTokenParameters, bool isOptional, System.Collections.Generic.IList<System.ServiceModel.Security.SupportingTokenProviderSpecification> providerSpecList) + 0xca bytes   
                        System.ServiceModel.dll!System.ServiceModel.Security.SecurityProtocol.OnOpen(System.TimeSpan timeout) + 0xa7 bytes   
                        System.ServiceModel.dll!System.ServiceModel.Security.SymmetricSecurityProtocol.OnOpen(System.TimeSpan timeout) + 0x45 bytes  
                        System.ServiceModel.dll!System.ServiceModel.Security.OperationWithTimeoutAsyncResult.OnScheduled(object state) + 0x82 bytes  
                        System.ServiceModel.dll!System.ServiceModel.Channels.IOThreadScheduler.CriticalHelper.WorkItem.Invoke2() + 0x46 bytes    
                        System.ServiceModel.dll!System.ServiceModel.Channels.IOThreadScheduler.CriticalHelper.WorkItem.OnSecurityContextCallback(object o) + 0x28 bytes  
                        ***mscorlib.dll!System.Security.SecurityContext.runTryCode(object userData) + 0x6e bytes     
                        [Native to Managed Transition]   
                        [Managed to Native Transition]   
                        ***mscorlib.dll!System.Security.SecurityContext.RunInternal(System.Security.SecurityContext securityContext, System.Threading.ContextCallback callBack, object state) + 0xc2 bytes   
                        ***mscorlib.dll!System.Security.SecurityContext.Run(System.Security.SecurityContext securityContext, System.Threading.ContextCallback callback, object state) + 0xca bytes   
                        System.ServiceModel.dll!System.ServiceModel.Channels.IOThreadScheduler.CriticalHelper.WorkItem.Invoke() + 0x4d bytes     
                        System.ServiceModel.dll!System.ServiceModel.Channels.IOThreadScheduler.CriticalHelper.ProcessCallbacks() + 0x180 bytes   
                        System.ServiceModel.dll!System.ServiceModel.Channels.IOThreadScheduler.CriticalHelper.CompletionCallback(object state) + 0x7a bytes  
                        System.ServiceModel.dll!System.ServiceModel.Channels.IOThreadScheduler.CriticalHelper.ScheduledOverlapped.IOCallback(uint errorCode, uint numBytes, System.Threading.NativeOverlapped* nativeOverlapped) + 0xf bytes     
                        SMDiagnostics.dll!System.ServiceModel.Diagnostics.Utility.IOCompletionThunk.UnhandledExceptionFrame(uint error, uint bytesRead, System.Threading.NativeOverlapped* nativeOverlapped) + 0x3d bytes    
                        mscorlib.dll!System.Threading._IOCompletionCallback.PerformIOCompletionCallback(uint errorCode, uint numBytes, System.Threading.NativeOverlapped* pOVERLAP) + 0x54 bytes     
                        [Appdomain Transition]   

1 Ответ

3 голосов
/ 02 мая 2011

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

If _proxy.State <> CommunicationState.Opened Then
     _proxy.Open()
End If

_proxy.Begin[asyncMethod]()

установка кэшированных учетных данных и проверка кэша токенов происходит синхронно, и, следовательно, с использованием правильного идентификатора и функционирования в соответствии с ожиданиями.

...