WCF Net.tcp с sspi терпит неудачу, если клиент и сервер не используют одинаковые окна. Identity
У меня есть служба WCF, обслуживаемая службой Windows. Клиентское приложение удастся, если я запишусь на клиентскую машину, используя те же учетные данные, что и служба, работающая под ней, но она не работает с исключением, если я вошел в систему с любой другой действительной учетной записью домена.
У меня есть две учетные записи, с которыми я тестирую: одна - обычная учетная запись пользователя, а другая - учетная запись администратора. Я пробовал все четыре комбинации, перечисленные ниже:
Server account
CLient RegUser AdminAcct
RegUser Succeeds Fails
AdminAcct Fails Succeeds
Как вы можете видеть, это не может быть проблемой администратора, поскольку система работает, когда клиент и сервер работают под учетной записью non-admin.
В обоих случаях, когда он терпит неудачу, я получаю такое же исключение на клиенте, не указав ничего из журналов сервера:
"Не удалось выполнить вызов SSPI. см. внутреннее исключение"
Внутреннее исключение . Неправильное имя целевого принципа.
Я зарегистрировал учетные записи как SPN.
Проблема возникает только из моего клиентского приложения, но не тогда, когда я использую WCVFTestClient.exe
, который поставляется с Visual Studio.
Исключение, в журнале трассировки WCF,
"System.ServiceModel.Security.SecurityNegotiationException, System.ServiceModel, Version = 4.0.0.0, Culture = нейтральный, PublicKeyToken = b77a5c561934e089"
с сообщением:
"Ошибка аутентификации на удаленной стороне (поток может быть доступен для дополнительных попыток аутентификации).
След стека находится внизу:
Что не так?
трассировка стека
System.ServiceModel.Channels.WindowsStreamSecurityUpgradeProvider.WindowsStreamSecurityUpgradeAcceptor.OnAcceptUpgrade(поток поток, SecurityMessageProperty & remoteSecurity) System.ServiceModel.Channels.StreamSecurityUpgradeAcceptorBase.AcceptUpgrade(Stream поток) System.ServiceModel.Channels.InitialServerConnectionReader.UpgradeConnection(IConnection соединение, StreamUpgradeAcceptor upgradeAcceptor, IDefaultCommunicationTimeouts defaultTimeouts) System.ServiceModel.Channels.ServerSessionPreambleConnectionReader.ServerFramingDuplexSessionChannel.OnOpen(TimeSpan тайм-аут) System.ServiceModel.Channels.CommunicationObject.Open(TimeSpan тайм-аут) System.ServiceModel.Dispatcher.ChannelHandler.OpenAndEnsurePump() System.Runtime.ActionItem.DefaultActionItem.TraceAndInvoke() System.Runtime.ActionItem.CallbackHelper.InvokeWithoutContext(Объект государство) System.Runtime.IOThreadScheduler.ScheduledOverlapped.IOCallback(UInt32 errorCode, UInt32 numBytes, NativeOverlapped * nativeOverlapped) System.Runtime.Fx.IOCompletionThunk.UnhandledExceptionFrame(UInt32 ошибка, UInt32 bytesRead, NativeOverlapped * nativeOverlapped) System.Threading._IOCompletionCallback.PerformIOCompletionCallback(UInt32 errorCode, UInt32 numBytes, NativeOverlapped * pOVERLAP)
Ответы
Ответ 1
Нашел ответ. Моя проблема заключалась в сочетании двух факторов.
-
При использовании бинарного протокола WCF net.tcp режим Client Security определяет, используется ли NTLM или Kerberos для аутентификации. Если вы установите режим безопасности клиента на "Транспорт", аутентификация использует NTLM, и возможен только один переход. Если вы попытаетесь поговорить с сервером WCF с третьим сервером (например, с базой данных), он будет терпеть неудачу. Использование SecurityMode = "Сообщение", otoh, заставляет сервер WCF использовать Kerberos, что позволяет несколько переходов...
-
Вторая проблема связана с тем, что я делал на клиенте в привязке. Протокол WCF net.tcp требует, чтобы при создании экземпляра конечной точки на клиенте вы должны указать "идентификатор конечной точки" (см. Код ниже). Я ошибочно предположил, что это как-то связано с аутентификацией и, следовательно, является личным именем текущего пользователя (Windows Principal) на клиенте.
var epId = EndpointIdentity.CreateUpnIdentity(userPrincipalName);
var ep = new EndpointAddress(new Uri(url), epId):
Нет... Идентификатор, который должен быть указан при создании конечной точки на клиенте, должен быть идентификатором, на котором работает сервер. Вот почему код работал всякий раз, когда я был зарегистрирован на клиенте с тем же пользователем, что и служба, работающая под ним, и не удалась, когда клиент был другим пользователем.
Я все еще не понимаю, почему этот идентификатор пользователя (учетной записи службы) должен быть указан в конечной точке на клиенте. Какими функциями на сервере нужны эти данные?
Ответ 2
Что касается второй точки, клиент не должен запускаться под той же учетной записью Windows сервера, что и для успешной аутентификации, и не требуется вручную указывать имя учетной записи в запросе. Из того, что я понимаю, аутентификация WCF является взаимной, то есть клиент также проверяет сервер.
Полученная ошибка, вероятно, связана с отказом на стороне клиента. Если вы настроите конечную точку обслуживания клиента для предоставления ServicePrincipalName, проблема может быть решена. Я нашел эту статью довольно полезной для решения той же проблемы, с которой я столкнулся.